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