diff options
Diffstat (limited to 'drivers/media/video/gspca/m5602/m5602_s5k4aa.c')
-rw-r--r-- | drivers/media/video/gspca/m5602/m5602_s5k4aa.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c new file mode 100644 index 00000000000..d27280be985 --- /dev/null +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c | |||
@@ -0,0 +1,721 @@ | |||
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 | |||
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); | ||
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); | ||
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); | ||
33 | |||
34 | static | ||
35 | const | ||
36 | struct dmi_system_id s5k4aa_vflip_dmi_table[] = { | ||
37 | { | ||
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 | }, { | ||
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 | } | ||
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 | } | ||
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 | } | ||
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 | } | ||
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 | } | ||
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 | } | ||
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 | } | ||
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 | } | ||
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 | } | ||
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 | } | ||
107 | }, { | ||
108 | .ident = "Lenovo Y300", | ||
109 | .matches = { | ||
110 | DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"), | ||
111 | DMI_MATCH(DMI_PRODUCT_NAME, "Y300") | ||
112 | } | ||
113 | }, | ||
114 | { } | ||
115 | }; | ||
116 | |||
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 | ||
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 | ||
139 | } | ||
140 | }; | ||
141 | |||
142 | static const struct ctrl s5k4aa_ctrls[] = { | ||
143 | #define VFLIP_IDX 0 | ||
144 | { | ||
145 | { | ||
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 | ||
153 | }, | ||
154 | .set = s5k4aa_set_vflip, | ||
155 | .get = s5k4aa_get_vflip | ||
156 | }, | ||
157 | #define HFLIP_IDX 1 | ||
158 | { | ||
159 | { | ||
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 | ||
167 | }, | ||
168 | .set = s5k4aa_set_hflip, | ||
169 | .get = s5k4aa_get_hflip | ||
170 | }, | ||
171 | #define GAIN_IDX 2 | ||
172 | { | ||
173 | { | ||
174 | .id = V4L2_CID_GAIN, | ||
175 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
176 | .name = "Gain", | ||
177 | .minimum = 0, | ||
178 | .maximum = 127, | ||
179 | .step = 1, | ||
180 | .default_value = S5K4AA_DEFAULT_GAIN, | ||
181 | .flags = V4L2_CTRL_FLAG_SLIDER | ||
182 | }, | ||
183 | .set = s5k4aa_set_gain, | ||
184 | .get = s5k4aa_get_gain | ||
185 | }, | ||
186 | #define EXPOSURE_IDX 3 | ||
187 | { | ||
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 | ||
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 | }, | ||
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 | |||
230 | }; | ||
231 | |||
232 | static void s5k4aa_dump_registers(struct sd *sd); | ||
233 | |||
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; | ||
239 | s32 *sensor_settings; | ||
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 | |||
251 | PDEBUG(D_PROBE, "Probing for a s5k4aa sensor"); | ||
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]; | ||
266 | err = m5602_write_sensor(sd, | ||
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]; | ||
274 | err = m5602_write_sensor(sd, | ||
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 */ | ||
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)) | ||
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"); | ||
296 | |||
297 | sensor_found: | ||
298 | sensor_settings = kmalloc( | ||
299 | ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL); | ||
300 | if (!sensor_settings) | ||
301 | return -ENOMEM; | ||
302 | |||
303 | sd->gspca_dev.cam.cam_mode = s5k4aa_modes; | ||
304 | sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes); | ||
305 | sd->desc->ctrls = s5k4aa_ctrls; | ||
306 | sd->desc->nctrls = ARRAY_SIZE(s5k4aa_ctrls); | ||
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; | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
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; | ||
320 | s32 *sensor_settings = sd->sensor_priv; | ||
321 | |||
322 | switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) { | ||
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 | } | ||
354 | err = s5k4aa_set_noise(&sd->gspca_dev, 0); | ||
355 | if (err < 0) | ||
356 | return err; | ||
357 | break; | ||
358 | |||
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 | } | ||
390 | err = s5k4aa_set_noise(&sd->gspca_dev, 1); | ||
391 | if (err < 0) | ||
392 | return err; | ||
393 | break; | ||
394 | } | ||
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]); | ||
421 | } | ||
422 | |||
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]; | ||
439 | err = m5602_write_sensor(sd, | ||
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]; | ||
446 | err = m5602_write_sensor(sd, | ||
447 | init_s5k4aa[i][1], data, 2); | ||
448 | break; | ||
449 | default: | ||
450 | info("Invalid stream command, exiting init"); | ||
451 | return -EINVAL; | ||
452 | } | ||
453 | } | ||
454 | |||
455 | if (dump_sensor) | ||
456 | s5k4aa_dump_registers(sd); | ||
457 | |||
458 | return err; | ||
459 | } | ||
460 | |||
461 | static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) | ||
462 | { | ||
463 | struct sd *sd = (struct sd *) gspca_dev; | ||
464 | s32 *sensor_settings = sd->sensor_priv; | ||
465 | |||
466 | *val = sensor_settings[EXPOSURE_IDX]; | ||
467 | PDEBUG(D_V4L2, "Read exposure %d", *val); | ||
468 | |||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) | ||
473 | { | ||
474 | struct sd *sd = (struct sd *) gspca_dev; | ||
475 | s32 *sensor_settings = sd->sensor_priv; | ||
476 | u8 data = S5K4AA_PAGE_MAP_2; | ||
477 | int err; | ||
478 | |||
479 | sensor_settings[EXPOSURE_IDX] = val; | ||
480 | PDEBUG(D_V4L2, "Set exposure to %d", val); | ||
481 | err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
482 | if (err < 0) | ||
483 | return err; | ||
484 | data = (val >> 8) & 0xff; | ||
485 | err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); | ||
486 | if (err < 0) | ||
487 | return err; | ||
488 | data = val & 0xff; | ||
489 | err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); | ||
490 | |||
491 | return err; | ||
492 | } | ||
493 | |||
494 | static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) | ||
495 | { | ||
496 | struct sd *sd = (struct sd *) gspca_dev; | ||
497 | s32 *sensor_settings = sd->sensor_priv; | ||
498 | |||
499 | *val = sensor_settings[VFLIP_IDX]; | ||
500 | PDEBUG(D_V4L2, "Read vertical flip %d", *val); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) | ||
506 | { | ||
507 | struct sd *sd = (struct sd *) gspca_dev; | ||
508 | s32 *sensor_settings = sd->sensor_priv; | ||
509 | u8 data = S5K4AA_PAGE_MAP_2; | ||
510 | int err; | ||
511 | |||
512 | sensor_settings[VFLIP_IDX] = val; | ||
513 | |||
514 | PDEBUG(D_V4L2, "Set vertical flip to %d", val); | ||
515 | err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
516 | if (err < 0) | ||
517 | return err; | ||
518 | |||
519 | err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
520 | if (err < 0) | ||
521 | return err; | ||
522 | |||
523 | if (dmi_check_system(s5k4aa_vflip_dmi_table)) | ||
524 | val = !val; | ||
525 | |||
526 | data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7)); | ||
527 | err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
528 | if (err < 0) | ||
529 | return err; | ||
530 | |||
531 | err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); | ||
532 | if (err < 0) | ||
533 | return err; | ||
534 | if (val) | ||
535 | data &= 0xfe; | ||
536 | else | ||
537 | data |= 0x01; | ||
538 | err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); | ||
539 | return err; | ||
540 | } | ||
541 | |||
542 | static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) | ||
543 | { | ||
544 | struct sd *sd = (struct sd *) gspca_dev; | ||
545 | s32 *sensor_settings = sd->sensor_priv; | ||
546 | |||
547 | *val = sensor_settings[HFLIP_IDX]; | ||
548 | PDEBUG(D_V4L2, "Read horizontal flip %d", *val); | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) | ||
554 | { | ||
555 | struct sd *sd = (struct sd *) gspca_dev; | ||
556 | s32 *sensor_settings = sd->sensor_priv; | ||
557 | u8 data = S5K4AA_PAGE_MAP_2; | ||
558 | int err; | ||
559 | |||
560 | sensor_settings[HFLIP_IDX] = val; | ||
561 | |||
562 | PDEBUG(D_V4L2, "Set horizontal flip to %d", val); | ||
563 | err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
564 | if (err < 0) | ||
565 | return err; | ||
566 | |||
567 | err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
568 | if (err < 0) | ||
569 | return err; | ||
570 | |||
571 | if (dmi_check_system(s5k4aa_vflip_dmi_table)) | ||
572 | val = !val; | ||
573 | |||
574 | data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6)); | ||
575 | err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
576 | if (err < 0) | ||
577 | return err; | ||
578 | |||
579 | err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); | ||
580 | if (err < 0) | ||
581 | return err; | ||
582 | if (val) | ||
583 | data &= 0xfe; | ||
584 | else | ||
585 | data |= 0x01; | ||
586 | err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); | ||
587 | return err; | ||
588 | } | ||
589 | |||
590 | static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) | ||
591 | { | ||
592 | struct sd *sd = (struct sd *) gspca_dev; | ||
593 | s32 *sensor_settings = sd->sensor_priv; | ||
594 | |||
595 | *val = sensor_settings[GAIN_IDX]; | ||
596 | PDEBUG(D_V4L2, "Read gain %d", *val); | ||
597 | return 0; | ||
598 | } | ||
599 | |||
600 | static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) | ||
601 | { | ||
602 | struct sd *sd = (struct sd *) gspca_dev; | ||
603 | s32 *sensor_settings = sd->sensor_priv; | ||
604 | u8 data = S5K4AA_PAGE_MAP_2; | ||
605 | int err; | ||
606 | |||
607 | sensor_settings[GAIN_IDX] = val; | ||
608 | |||
609 | PDEBUG(D_V4L2, "Set gain to %d", val); | ||
610 | err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
611 | if (err < 0) | ||
612 | return err; | ||
613 | |||
614 | data = val & 0xff; | ||
615 | err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1); | ||
616 | |||
617 | return err; | ||
618 | } | ||
619 | |||
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 | |||
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 | |||
676 | void s5k4aa_disconnect(struct sd *sd) | ||
677 | { | ||
678 | sd->sensor = NULL; | ||
679 | kfree(sd->sensor_priv); | ||
680 | } | ||
681 | |||
682 | static void s5k4aa_dump_registers(struct sd *sd) | ||
683 | { | ||
684 | int address; | ||
685 | u8 page, old_page; | ||
686 | m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); | ||
687 | for (page = 0; page < 16; page++) { | ||
688 | m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); | ||
689 | info("Dumping the s5k4aa register state for page 0x%x", page); | ||
690 | for (address = 0; address <= 0xff; address++) { | ||
691 | u8 value = 0; | ||
692 | m5602_read_sensor(sd, address, &value, 1); | ||
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++) { | ||
700 | m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); | ||
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 | |||
706 | m5602_read_sensor(sd, address, &old_value, 1); | ||
707 | m5602_write_sensor(sd, address, &test_value, 1); | ||
708 | m5602_read_sensor(sd, address, &ctrl_value, 1); | ||
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 */ | ||
716 | m5602_write_sensor(sd, address, &old_value, 1); | ||
717 | } | ||
718 | } | ||
719 | info("Read/write register probing complete"); | ||
720 | m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); | ||
721 | } | ||