1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Driver for the s5k4aa sensor
4 *
5 * Copyright (C) 2008 Erik Andrén
6 * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
7 * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
8 *
9 * Portions of code to USB interface and ALi driver software,
10 * Copyright (c) 2006 Willem Duinker
11 * v4l2 interface modeled after the V4L2 driver
12 * for SN9C10x PC Camera Controllers
13 */
14
15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16
17#include "m5602_s5k4aa.h"
18
19static const unsigned char preinit_s5k4aa[][4] = {
20	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
21	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
22	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
23	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
24	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
25	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
26	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
27
28	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
29	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
30	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
31	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
32	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
33	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
34	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
35	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
36	{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
37	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
38	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
39	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
40	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
41	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
42	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
43	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
44
45	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
46	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
47	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
48	{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
49	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
50	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
51	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
52	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
53	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
54	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
55	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
56	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
57	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
58
59	{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
60};
61
62static const unsigned char init_s5k4aa[][4] = {
63	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
64	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
65	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
66	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
67	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
68	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
69	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
70
71	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
72	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
73	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
74	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
75	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
76	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
77	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
78	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
79	{BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
80	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
81	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
82	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
83	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
84	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
85	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
86	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
87
88	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
89	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
90	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
91	{BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
92	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
93	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
94	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
95	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
96	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
97	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
98	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
99	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
100	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
101
102	{SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
103	{SENSOR, 0x36, 0x01, 0x00},
104	{SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
105	{SENSOR, 0x7b, 0xff, 0x00},
106	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
107	{SENSOR, 0x0c, 0x05, 0x00},
108	{SENSOR, 0x02, 0x0e, 0x00},
109	{SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
110	{SENSOR, 0x37, 0x00, 0x00},
111};
112
113static const unsigned char VGA_s5k4aa[][4] = {
114	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
115	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
116	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
117	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
118	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
119	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
120	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
121	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
122	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
123	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
124	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
125	/* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
126	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
127	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
128	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
129	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
130	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
131	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
132	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
133	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
134	/* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
135	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
136	{BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
137	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
138	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
139	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
140
141	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
142	{SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
143		| S5K4AA_RM_COL_SKIP_2X, 0x00},
144	/* 0x37 : Fix image stability when light is too bright and improves
145	 * image quality in 640x480, but worsens it in 1280x1024 */
146	{SENSOR, 0x37, 0x01, 0x00},
147	/* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
148	{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
149	{SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00},
150	{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
151	{SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
152	/* window_height_hi, window_height_lo : 960 = 0x03c0 */
153	{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
154	{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
155	/* window_width_hi, window_width_lo : 1280 = 0x0500 */
156	{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
157	{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
158	{SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
159	{SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
160	{SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
161	{SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
162	{SENSOR, 0x11, 0x04, 0x00},
163	{SENSOR, 0x12, 0xc3, 0x00},
164	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
165	{SENSOR, 0x02, 0x0e, 0x00},
166};
167
168static const unsigned char SXGA_s5k4aa[][4] = {
169	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
170	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
171	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
172	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
173	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
174	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
175	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
176	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
177	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
178	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
179	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
180	/* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */
181	{BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
182	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
183	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
184	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
185	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
186	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
187	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
188	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
189	/* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */
190	{BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
191	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
192	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
193	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
194	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
195
196	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
197	{SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00},
198	{SENSOR, 0x37, 0x01, 0x00},
199	{SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
200	{SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00},
201	{SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
202	{SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00},
203	{SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00},
204	{SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00},
205	{SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
206	{SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
207	{SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00},
208	{SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00},
209	{SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
210	{SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
211	{SENSOR, 0x11, 0x04, 0x00},
212	{SENSOR, 0x12, 0xc3, 0x00},
213	{SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
214	{SENSOR, 0x02, 0x0e, 0x00},
215};
216
217
218static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl);
219static void s5k4aa_dump_registers(struct sd *sd);
220
221static const struct v4l2_ctrl_ops s5k4aa_ctrl_ops = {
222	.s_ctrl = s5k4aa_s_ctrl,
223};
224
225static
226    const
227	struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
228	{
229		.ident = "BRUNEINIT",
230		.matches = {
231			DMI_MATCH(DMI_SYS_VENDOR, "BRUNENIT"),
232			DMI_MATCH(DMI_PRODUCT_NAME, "BRUNENIT"),
233			DMI_MATCH(DMI_BOARD_VERSION, "00030D0000000001")
234		}
235	}, {
236		.ident = "Fujitsu-Siemens Amilo Xa 2528",
237		.matches = {
238			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
239			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
240		}
241	}, {
242		.ident = "Fujitsu-Siemens Amilo Xi 2428",
243		.matches = {
244			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
245			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2428")
246		}
247	}, {
248		.ident = "Fujitsu-Siemens Amilo Xi 2528",
249		.matches = {
250			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
251			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2528")
252		}
253	}, {
254		.ident = "Fujitsu-Siemens Amilo Xi 2550",
255		.matches = {
256			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
257			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
258		}
259	}, {
260		.ident = "Fujitsu-Siemens Amilo Pa 2548",
261		.matches = {
262			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
263			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548")
264		}
265	}, {
266		.ident = "Fujitsu-Siemens Amilo Pi 2530",
267		.matches = {
268			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
269			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 2530")
270		}
271	}, {
272		.ident = "MSI GX700",
273		.matches = {
274			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
275			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
276			DMI_MATCH(DMI_BIOS_DATE, "12/02/2008")
277		}
278	}, {
279		.ident = "MSI GX700",
280		.matches = {
281			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
282			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
283			DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
284		}
285	}, {
286		.ident = "MSI GX700",
287		.matches = {
288			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
289			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
290			DMI_MATCH(DMI_BIOS_DATE, "07/19/2007")
291		}
292	}, {
293		.ident = "MSI GX700/GX705/EX700",
294		.matches = {
295			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
296			DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")
297		}
298	}, {
299		.ident = "MSI L735",
300		.matches = {
301			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
302			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1717X")
303		}
304	}, {
305		.ident = "Lenovo Y300",
306		.matches = {
307			DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"),
308			DMI_MATCH(DMI_PRODUCT_NAME, "Y300")
309		}
310	},
311	{ }
312};
313
314static struct v4l2_pix_format s5k4aa_modes[] = {
315	{
316		640,
317		480,
318		V4L2_PIX_FMT_SBGGR8,
319		V4L2_FIELD_NONE,
320		.sizeimage =
321			640 * 480,
322		.bytesperline = 640,
323		.colorspace = V4L2_COLORSPACE_SRGB,
324		.priv = 0
325	},
326	{
327		1280,
328		1024,
329		V4L2_PIX_FMT_SBGGR8,
330		V4L2_FIELD_NONE,
331		.sizeimage =
332			1280 * 1024,
333		.bytesperline = 1280,
334		.colorspace = V4L2_COLORSPACE_SRGB,
335		.priv = 0
336	}
337};
338
339int s5k4aa_probe(struct sd *sd)
340{
341	u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
342	const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};
343	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
344	int i, err = 0;
345
346	if (force_sensor) {
347		if (force_sensor == S5K4AA_SENSOR) {
348			pr_info("Forcing a %s sensor\n", s5k4aa.name);
349			goto sensor_found;
350		}
351		/* If we want to force another sensor, don't try to probe this
352		 * one */
353		return -ENODEV;
354	}
355
356	gspca_dbg(gspca_dev, D_PROBE, "Probing for a s5k4aa sensor\n");
357
358	/* Preinit the sensor */
359	for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
360		u8 data[2] = {0x00, 0x00};
361
362		switch (preinit_s5k4aa[i][0]) {
363		case BRIDGE:
364			err = m5602_write_bridge(sd,
365						 preinit_s5k4aa[i][1],
366						 preinit_s5k4aa[i][2]);
367			break;
368
369		case SENSOR:
370			data[0] = preinit_s5k4aa[i][2];
371			err = m5602_write_sensor(sd,
372						  preinit_s5k4aa[i][1],
373						  data, 1);
374			break;
375
376		case SENSOR_LONG:
377			data[0] = preinit_s5k4aa[i][2];
378			data[1] = preinit_s5k4aa[i][3];
379			err = m5602_write_sensor(sd,
380						  preinit_s5k4aa[i][1],
381						  data, 2);
382			break;
383		default:
384			pr_info("Invalid stream command, exiting init\n");
385			return -EINVAL;
386		}
387	}
388
389	/* Test some registers, but we don't know their exact meaning yet */
390	if (m5602_read_sensor(sd, 0x00, prod_id, 2))
391		return -ENODEV;
392	if (m5602_read_sensor(sd, 0x02, prod_id+2, 2))
393		return -ENODEV;
394	if (m5602_read_sensor(sd, 0x04, prod_id+4, 2))
395		return -ENODEV;
396
397	if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
398		return -ENODEV;
399	else
400		pr_info("Detected a s5k4aa sensor\n");
401
402sensor_found:
403	sd->gspca_dev.cam.cam_mode = s5k4aa_modes;
404	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes);
405
406	return 0;
407}
408
409int s5k4aa_start(struct sd *sd)
410{
411	int i, err = 0;
412	u8 data[2];
413	struct cam *cam = &sd->gspca_dev.cam;
414	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
415
416	switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) {
417	case 1280:
418		gspca_dbg(gspca_dev, D_CONF, "Configuring camera for SXGA mode\n");
419
420		for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) {
421			switch (SXGA_s5k4aa[i][0]) {
422			case BRIDGE:
423				err = m5602_write_bridge(sd,
424						 SXGA_s5k4aa[i][1],
425						 SXGA_s5k4aa[i][2]);
426			break;
427
428			case SENSOR:
429				data[0] = SXGA_s5k4aa[i][2];
430				err = m5602_write_sensor(sd,
431						 SXGA_s5k4aa[i][1],
432						 data, 1);
433			break;
434
435			case SENSOR_LONG:
436				data[0] = SXGA_s5k4aa[i][2];
437				data[1] = SXGA_s5k4aa[i][3];
438				err = m5602_write_sensor(sd,
439						  SXGA_s5k4aa[i][1],
440						  data, 2);
441			break;
442
443			default:
444				pr_err("Invalid stream command, exiting init\n");
445				return -EINVAL;
446			}
447		}
448		break;
449
450	case 640:
451		gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n");
452
453		for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) {
454			switch (VGA_s5k4aa[i][0]) {
455			case BRIDGE:
456				err = m5602_write_bridge(sd,
457						 VGA_s5k4aa[i][1],
458						 VGA_s5k4aa[i][2]);
459			break;
460
461			case SENSOR:
462				data[0] = VGA_s5k4aa[i][2];
463				err = m5602_write_sensor(sd,
464						 VGA_s5k4aa[i][1],
465						 data, 1);
466			break;
467
468			case SENSOR_LONG:
469				data[0] = VGA_s5k4aa[i][2];
470				data[1] = VGA_s5k4aa[i][3];
471				err = m5602_write_sensor(sd,
472						  VGA_s5k4aa[i][1],
473						  data, 2);
474			break;
475
476			default:
477				pr_err("Invalid stream command, exiting init\n");
478				return -EINVAL;
479			}
480		}
481		break;
482	}
483	if (err < 0)
484		return err;
485
486	return 0;
487}
488
489int s5k4aa_init(struct sd *sd)
490{
491	int i, err = 0;
492
493	for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {
494		u8 data[2] = {0x00, 0x00};
495
496		switch (init_s5k4aa[i][0]) {
497		case BRIDGE:
498			err = m5602_write_bridge(sd,
499				init_s5k4aa[i][1],
500				init_s5k4aa[i][2]);
501			break;
502
503		case SENSOR:
504			data[0] = init_s5k4aa[i][2];
505			err = m5602_write_sensor(sd,
506				init_s5k4aa[i][1], data, 1);
507			break;
508
509		case SENSOR_LONG:
510			data[0] = init_s5k4aa[i][2];
511			data[1] = init_s5k4aa[i][3];
512			err = m5602_write_sensor(sd,
513				init_s5k4aa[i][1], data, 2);
514			break;
515		default:
516			pr_info("Invalid stream command, exiting init\n");
517			return -EINVAL;
518		}
519	}
520
521	if (dump_sensor)
522		s5k4aa_dump_registers(sd);
523
524	return err;
525}
526
527int s5k4aa_init_controls(struct sd *sd)
528{
529	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
530
531	sd->gspca_dev.vdev.ctrl_handler = hdl;
532	v4l2_ctrl_handler_init(hdl, 6);
533
534	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_BRIGHTNESS,
535			  0, 0x1f, 1, S5K4AA_DEFAULT_BRIGHTNESS);
536
537	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_EXPOSURE,
538			  13, 0xfff, 1, 0x100);
539
540	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_GAIN,
541			  0, 127, 1, S5K4AA_DEFAULT_GAIN);
542
543	v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_SHARPNESS,
544			  0, 1, 1, 1);
545
546	sd->hflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_HFLIP,
547				      0, 1, 1, 0);
548	sd->vflip = v4l2_ctrl_new_std(hdl, &s5k4aa_ctrl_ops, V4L2_CID_VFLIP,
549				      0, 1, 1, 0);
550
551	if (hdl->error) {
552		pr_err("Could not initialize controls\n");
553		return hdl->error;
554	}
555
556	v4l2_ctrl_cluster(2, &sd->hflip);
557
558	return 0;
559}
560
561static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
562{
563	struct sd *sd = (struct sd *) gspca_dev;
564	u8 data = S5K4AA_PAGE_MAP_2;
565	int err;
566
567	gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val);
568	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
569	if (err < 0)
570		return err;
571	data = (val >> 8) & 0xff;
572	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
573	if (err < 0)
574		return err;
575	data = val & 0xff;
576	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
577
578	return err;
579}
580
581static int s5k4aa_set_hvflip(struct gspca_dev *gspca_dev)
582{
583	struct sd *sd = (struct sd *) gspca_dev;
584	u8 data = S5K4AA_PAGE_MAP_2;
585	int err;
586	int hflip = sd->hflip->val;
587	int vflip = sd->vflip->val;
588
589	gspca_dbg(gspca_dev, D_CONF, "Set hvflip %d %d\n", hflip, vflip);
590	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
591	if (err < 0)
592		return err;
593
594	err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
595	if (err < 0)
596		return err;
597
598	if (dmi_check_system(s5k4aa_vflip_dmi_table)) {
599		hflip = !hflip;
600		vflip = !vflip;
601	}
602
603	data = (data & 0x7f) | (vflip << 7) | (hflip << 6);
604	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
605	if (err < 0)
606		return err;
607
608	err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
609	if (err < 0)
610		return err;
611	if (hflip)
612		data &= 0xfe;
613	else
614		data |= 0x01;
615	err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
616	if (err < 0)
617		return err;
618
619	err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
620	if (err < 0)
621		return err;
622	if (vflip)
623		data &= 0xfe;
624	else
625		data |= 0x01;
626	err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
627	if (err < 0)
628		return err;
629
630	return 0;
631}
632
633static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
634{
635	struct sd *sd = (struct sd *) gspca_dev;
636	u8 data = S5K4AA_PAGE_MAP_2;
637	int err;
638
639	gspca_dbg(gspca_dev, D_CONF, "Set gain to %d\n", val);
640	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
641	if (err < 0)
642		return err;
643
644	data = val & 0xff;
645	err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1);
646
647	return err;
648}
649
650static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
651{
652	struct sd *sd = (struct sd *) gspca_dev;
653	u8 data = S5K4AA_PAGE_MAP_2;
654	int err;
655
656	gspca_dbg(gspca_dev, D_CONF, "Set brightness to %d\n", val);
657	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
658	if (err < 0)
659		return err;
660
661	data = val & 0xff;
662	return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1);
663}
664
665static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val)
666{
667	struct sd *sd = (struct sd *) gspca_dev;
668	u8 data = S5K4AA_PAGE_MAP_2;
669	int err;
670
671	gspca_dbg(gspca_dev, D_CONF, "Set noise to %d\n", val);
672	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
673	if (err < 0)
674		return err;
675
676	data = val & 0x01;
677	return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1);
678}
679
680static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl)
681{
682	struct gspca_dev *gspca_dev =
683		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
684	int err;
685
686	if (!gspca_dev->streaming)
687		return 0;
688
689	switch (ctrl->id) {
690	case V4L2_CID_BRIGHTNESS:
691		err = s5k4aa_set_brightness(gspca_dev, ctrl->val);
692		break;
693	case V4L2_CID_EXPOSURE:
694		err = s5k4aa_set_exposure(gspca_dev, ctrl->val);
695		break;
696	case V4L2_CID_GAIN:
697		err = s5k4aa_set_gain(gspca_dev, ctrl->val);
698		break;
699	case V4L2_CID_SHARPNESS:
700		err = s5k4aa_set_noise(gspca_dev, ctrl->val);
701		break;
702	case V4L2_CID_HFLIP:
703		err = s5k4aa_set_hvflip(gspca_dev);
704		break;
705	default:
706		return -EINVAL;
707	}
708
709	return err;
710}
711
712void s5k4aa_disconnect(struct sd *sd)
713{
714	sd->sensor = NULL;
715}
716
717static void s5k4aa_dump_registers(struct sd *sd)
718{
719	int address;
720	u8 page, old_page;
721	m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
722	for (page = 0; page < 16; page++) {
723		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
724		pr_info("Dumping the s5k4aa register state for page 0x%x\n",
725			page);
726		for (address = 0; address <= 0xff; address++) {
727			u8 value = 0;
728			m5602_read_sensor(sd, address, &value, 1);
729			pr_info("register 0x%x contains 0x%x\n",
730				address, value);
731		}
732	}
733	pr_info("s5k4aa register state dump complete\n");
734
735	for (page = 0; page < 16; page++) {
736		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
737		pr_info("Probing for which registers that are read/write for page 0x%x\n",
738			page);
739		for (address = 0; address <= 0xff; address++) {
740			u8 old_value, ctrl_value, test_value = 0xff;
741
742			m5602_read_sensor(sd, address, &old_value, 1);
743			m5602_write_sensor(sd, address, &test_value, 1);
744			m5602_read_sensor(sd, address, &ctrl_value, 1);
745
746			if (ctrl_value == test_value)
747				pr_info("register 0x%x is writeable\n",
748					address);
749			else
750				pr_info("register 0x%x is read only\n",
751					address);
752
753			/* Restore original value */
754			m5602_write_sensor(sd, address, &old_value, 1);
755		}
756	}
757	pr_info("Read/write register probing complete\n");
758	m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
759}
760