diff options
Diffstat (limited to 'drivers/media/video/gspca/m5602/m5602_s5k4aa.c')
-rw-r--r-- | drivers/media/video/gspca/m5602/m5602_s5k4aa.c | 460 |
1 files changed, 460 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..3a2ae7a2e26 --- /dev/null +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c | |||
@@ -0,0 +1,460 @@ | |||
1 | /* | ||
2 | * Driver for the s5k4aa sensor | ||
3 | * | ||
4 | * Copyright (C) 2008 Erik Andren | ||
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 | int s5k4aa_probe(struct sd *sd) | ||
22 | { | ||
23 | u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
24 | const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75}; | ||
25 | int i, err = 0; | ||
26 | |||
27 | if (force_sensor) { | ||
28 | if (force_sensor == S5K4AA_SENSOR) { | ||
29 | info("Forcing a %s sensor", s5k4aa.name); | ||
30 | goto sensor_found; | ||
31 | } | ||
32 | /* If we want to force another sensor, don't try to probe this | ||
33 | * one */ | ||
34 | return -ENODEV; | ||
35 | } | ||
36 | |||
37 | info("Probing for a s5k4aa sensor"); | ||
38 | |||
39 | /* Preinit the sensor */ | ||
40 | for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) { | ||
41 | u8 data[2] = {0x00, 0x00}; | ||
42 | |||
43 | switch (preinit_s5k4aa[i][0]) { | ||
44 | case BRIDGE: | ||
45 | err = m5602_write_bridge(sd, | ||
46 | preinit_s5k4aa[i][1], | ||
47 | preinit_s5k4aa[i][2]); | ||
48 | break; | ||
49 | |||
50 | case SENSOR: | ||
51 | data[0] = preinit_s5k4aa[i][2]; | ||
52 | err = s5k4aa_write_sensor(sd, | ||
53 | preinit_s5k4aa[i][1], | ||
54 | data, 1); | ||
55 | break; | ||
56 | |||
57 | case SENSOR_LONG: | ||
58 | data[0] = preinit_s5k4aa[i][2]; | ||
59 | data[1] = preinit_s5k4aa[i][3]; | ||
60 | err = s5k4aa_write_sensor(sd, | ||
61 | preinit_s5k4aa[i][1], | ||
62 | data, 2); | ||
63 | break; | ||
64 | default: | ||
65 | info("Invalid stream command, exiting init"); | ||
66 | return -EINVAL; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /* Test some registers, but we don't know their exact meaning yet */ | ||
71 | if (s5k4aa_read_sensor(sd, 0x00, prod_id, sizeof(prod_id))) | ||
72 | return -ENODEV; | ||
73 | |||
74 | if (memcmp(prod_id, expected_prod_id, sizeof(prod_id))) | ||
75 | return -ENODEV; | ||
76 | else | ||
77 | info("Detected a s5k4aa sensor"); | ||
78 | sensor_found: | ||
79 | sd->gspca_dev.cam.cam_mode = s5k4aa.modes; | ||
80 | sd->gspca_dev.cam.nmodes = s5k4aa.nmodes; | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | int s5k4aa_read_sensor(struct sd *sd, const u8 address, | ||
85 | u8 *i2c_data, const u8 len) | ||
86 | { | ||
87 | int err, i; | ||
88 | |||
89 | do { | ||
90 | err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); | ||
91 | } while ((*i2c_data & I2C_BUSY) && !err); | ||
92 | if (err < 0) | ||
93 | goto out; | ||
94 | |||
95 | err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, | ||
96 | sd->sensor->i2c_slave_id); | ||
97 | if (err < 0) | ||
98 | goto out; | ||
99 | |||
100 | err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); | ||
101 | if (err < 0) | ||
102 | goto out; | ||
103 | |||
104 | err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); | ||
105 | if (err < 0) | ||
106 | goto out; | ||
107 | |||
108 | do { | ||
109 | err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); | ||
110 | } while ((*i2c_data & I2C_BUSY) && !err); | ||
111 | if (err < 0) | ||
112 | goto out; | ||
113 | |||
114 | for (i = 0; (i < len) & !err; i++) { | ||
115 | err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); | ||
116 | |||
117 | PDEBUG(DBG_TRACE, "Reading sensor register " | ||
118 | "0x%x containing 0x%x ", address, *i2c_data); | ||
119 | } | ||
120 | out: | ||
121 | return (err < 0) ? err : 0; | ||
122 | } | ||
123 | |||
124 | int s5k4aa_write_sensor(struct sd *sd, const u8 address, | ||
125 | u8 *i2c_data, const u8 len) | ||
126 | { | ||
127 | int err, i; | ||
128 | u8 *p; | ||
129 | struct usb_device *udev = sd->gspca_dev.dev; | ||
130 | __u8 *buf = sd->gspca_dev.usb_buf; | ||
131 | |||
132 | /* No sensor with a data width larger than 16 bits has yet been seen */ | ||
133 | if (len > 2 || !len) | ||
134 | return -EINVAL; | ||
135 | |||
136 | memcpy(buf, sensor_urb_skeleton, | ||
137 | sizeof(sensor_urb_skeleton)); | ||
138 | |||
139 | buf[11] = sd->sensor->i2c_slave_id; | ||
140 | buf[15] = address; | ||
141 | |||
142 | /* Special case larger sensor writes */ | ||
143 | p = buf + 16; | ||
144 | |||
145 | /* Copy a four byte write sequence for each byte to be written to */ | ||
146 | for (i = 0; i < len; i++) { | ||
147 | memcpy(p, sensor_urb_skeleton + 16, 4); | ||
148 | p[3] = i2c_data[i]; | ||
149 | p += 4; | ||
150 | PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x", | ||
151 | address, i2c_data[i]); | ||
152 | } | ||
153 | |||
154 | /* Copy the tailer */ | ||
155 | memcpy(p, sensor_urb_skeleton + 20, 4); | ||
156 | |||
157 | /* Set the total length */ | ||
158 | p[3] = 0x10 + len; | ||
159 | |||
160 | err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | ||
161 | 0x04, 0x40, 0x19, | ||
162 | 0x0000, buf, | ||
163 | 20 + len * 4, M5602_URB_MSG_TIMEOUT); | ||
164 | |||
165 | return (err < 0) ? err : 0; | ||
166 | } | ||
167 | |||
168 | int s5k4aa_init(struct sd *sd) | ||
169 | { | ||
170 | int i, err = 0; | ||
171 | |||
172 | for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) { | ||
173 | u8 data[2] = {0x00, 0x00}; | ||
174 | |||
175 | switch (init_s5k4aa[i][0]) { | ||
176 | case BRIDGE: | ||
177 | err = m5602_write_bridge(sd, | ||
178 | init_s5k4aa[i][1], | ||
179 | init_s5k4aa[i][2]); | ||
180 | break; | ||
181 | |||
182 | case SENSOR: | ||
183 | data[0] = init_s5k4aa[i][2]; | ||
184 | err = s5k4aa_write_sensor(sd, | ||
185 | init_s5k4aa[i][1], data, 1); | ||
186 | break; | ||
187 | |||
188 | case SENSOR_LONG: | ||
189 | data[0] = init_s5k4aa[i][2]; | ||
190 | data[1] = init_s5k4aa[i][3]; | ||
191 | err = s5k4aa_write_sensor(sd, | ||
192 | init_s5k4aa[i][1], data, 2); | ||
193 | break; | ||
194 | default: | ||
195 | info("Invalid stream command, exiting init"); | ||
196 | return -EINVAL; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | if (dump_sensor) | ||
201 | s5k4aa_dump_registers(sd); | ||
202 | |||
203 | if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) { | ||
204 | u8 data = 0x02; | ||
205 | info("vertical flip quirk active"); | ||
206 | s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
207 | s5k4aa_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
208 | data |= S5K4AA_RM_V_FLIP; | ||
209 | data &= ~S5K4AA_RM_H_FLIP; | ||
210 | s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
211 | |||
212 | /* Decrement COLSTART to preserve color order (BGGR) */ | ||
213 | s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); | ||
214 | data--; | ||
215 | s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); | ||
216 | |||
217 | /* Increment ROWSTART to preserve color order (BGGR) */ | ||
218 | s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); | ||
219 | data++; | ||
220 | s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); | ||
221 | } | ||
222 | |||
223 | return (err < 0) ? err : 0; | ||
224 | } | ||
225 | |||
226 | int s5k4aa_power_down(struct sd *sd) | ||
227 | { | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) | ||
232 | { | ||
233 | struct sd *sd = (struct sd *) gspca_dev; | ||
234 | u8 data = S5K4AA_PAGE_MAP_2; | ||
235 | int err; | ||
236 | |||
237 | err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
238 | if (err < 0) | ||
239 | goto out; | ||
240 | |||
241 | err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); | ||
242 | if (err < 0) | ||
243 | goto out; | ||
244 | |||
245 | *val = data << 8; | ||
246 | err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); | ||
247 | *val |= data; | ||
248 | PDEBUG(DBG_V4L2_CID, "Read exposure %d", *val); | ||
249 | out: | ||
250 | return (err < 0) ? err : 0; | ||
251 | } | ||
252 | |||
253 | int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) | ||
254 | { | ||
255 | struct sd *sd = (struct sd *) gspca_dev; | ||
256 | u8 data = S5K4AA_PAGE_MAP_2; | ||
257 | int err; | ||
258 | |||
259 | PDEBUG(DBG_V4L2_CID, "Set exposure to %d", val); | ||
260 | err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
261 | if (err < 0) | ||
262 | goto out; | ||
263 | data = (val >> 8) & 0xff; | ||
264 | err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); | ||
265 | if (err < 0) | ||
266 | goto out; | ||
267 | data = val & 0xff; | ||
268 | err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); | ||
269 | out: | ||
270 | return (err < 0) ? err : 0; | ||
271 | } | ||
272 | |||
273 | int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) | ||
274 | { | ||
275 | struct sd *sd = (struct sd *) gspca_dev; | ||
276 | u8 data = S5K4AA_PAGE_MAP_2; | ||
277 | int err; | ||
278 | |||
279 | err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
280 | if (err < 0) | ||
281 | goto out; | ||
282 | |||
283 | err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
284 | *val = (data & S5K4AA_RM_V_FLIP) >> 7; | ||
285 | PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val); | ||
286 | |||
287 | out: | ||
288 | return (err < 0) ? err : 0; | ||
289 | } | ||
290 | |||
291 | int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) | ||
292 | { | ||
293 | struct sd *sd = (struct sd *) gspca_dev; | ||
294 | u8 data = S5K4AA_PAGE_MAP_2; | ||
295 | int err; | ||
296 | |||
297 | PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val); | ||
298 | err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
299 | if (err < 0) | ||
300 | goto out; | ||
301 | err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
302 | if (err < 0) | ||
303 | goto out; | ||
304 | data = ((data & ~S5K4AA_RM_V_FLIP) | ||
305 | | ((val & 0x01) << 7)); | ||
306 | err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
307 | if (err < 0) | ||
308 | goto out; | ||
309 | |||
310 | if (val) { | ||
311 | err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); | ||
312 | if (err < 0) | ||
313 | goto out; | ||
314 | |||
315 | data++; | ||
316 | err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); | ||
317 | } else { | ||
318 | err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); | ||
319 | if (err < 0) | ||
320 | goto out; | ||
321 | |||
322 | data--; | ||
323 | err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); | ||
324 | } | ||
325 | out: | ||
326 | return (err < 0) ? err : 0; | ||
327 | } | ||
328 | |||
329 | int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) | ||
330 | { | ||
331 | struct sd *sd = (struct sd *) gspca_dev; | ||
332 | u8 data = S5K4AA_PAGE_MAP_2; | ||
333 | int err; | ||
334 | |||
335 | err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
336 | if (err < 0) | ||
337 | goto out; | ||
338 | |||
339 | err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
340 | *val = (data & S5K4AA_RM_H_FLIP) >> 6; | ||
341 | PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val); | ||
342 | out: | ||
343 | return (err < 0) ? err : 0; | ||
344 | } | ||
345 | |||
346 | int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) | ||
347 | { | ||
348 | struct sd *sd = (struct sd *) gspca_dev; | ||
349 | u8 data = S5K4AA_PAGE_MAP_2; | ||
350 | int err; | ||
351 | |||
352 | PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d", | ||
353 | val); | ||
354 | err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
355 | if (err < 0) | ||
356 | goto out; | ||
357 | err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
358 | if (err < 0) | ||
359 | goto out; | ||
360 | |||
361 | data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6)); | ||
362 | err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); | ||
363 | if (err < 0) | ||
364 | goto out; | ||
365 | |||
366 | if (val) { | ||
367 | err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); | ||
368 | if (err < 0) | ||
369 | goto out; | ||
370 | data++; | ||
371 | err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); | ||
372 | if (err < 0) | ||
373 | goto out; | ||
374 | } else { | ||
375 | err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); | ||
376 | if (err < 0) | ||
377 | goto out; | ||
378 | data--; | ||
379 | err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); | ||
380 | } | ||
381 | out: | ||
382 | return (err < 0) ? err : 0; | ||
383 | } | ||
384 | |||
385 | int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) | ||
386 | { | ||
387 | struct sd *sd = (struct sd *) gspca_dev; | ||
388 | u8 data = S5K4AA_PAGE_MAP_2; | ||
389 | int err; | ||
390 | |||
391 | err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
392 | if (err < 0) | ||
393 | goto out; | ||
394 | |||
395 | err = s5k4aa_read_sensor(sd, S5K4AA_GAIN_2, &data, 1); | ||
396 | *val = data; | ||
397 | PDEBUG(DBG_V4L2_CID, "Read gain %d", *val); | ||
398 | |||
399 | out: | ||
400 | return (err < 0) ? err : 0; | ||
401 | } | ||
402 | |||
403 | int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) | ||
404 | { | ||
405 | struct sd *sd = (struct sd *) gspca_dev; | ||
406 | u8 data = S5K4AA_PAGE_MAP_2; | ||
407 | int err; | ||
408 | |||
409 | PDEBUG(DBG_V4L2_CID, "Set gain to %d", val); | ||
410 | err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); | ||
411 | if (err < 0) | ||
412 | goto out; | ||
413 | |||
414 | data = val & 0xff; | ||
415 | err = s5k4aa_write_sensor(sd, S5K4AA_GAIN_2, &data, 1); | ||
416 | |||
417 | out: | ||
418 | return (err < 0) ? err : 0; | ||
419 | } | ||
420 | |||
421 | void s5k4aa_dump_registers(struct sd *sd) | ||
422 | { | ||
423 | int address; | ||
424 | u8 page, old_page; | ||
425 | s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); | ||
426 | for (page = 0; page < 16; page++) { | ||
427 | s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); | ||
428 | info("Dumping the s5k4aa register state for page 0x%x", page); | ||
429 | for (address = 0; address <= 0xff; address++) { | ||
430 | u8 value = 0; | ||
431 | s5k4aa_read_sensor(sd, address, &value, 1); | ||
432 | info("register 0x%x contains 0x%x", | ||
433 | address, value); | ||
434 | } | ||
435 | } | ||
436 | info("s5k4aa register state dump complete"); | ||
437 | |||
438 | for (page = 0; page < 16; page++) { | ||
439 | s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); | ||
440 | info("Probing for which registers that are " | ||
441 | "read/write for page 0x%x", page); | ||
442 | for (address = 0; address <= 0xff; address++) { | ||
443 | u8 old_value, ctrl_value, test_value = 0xff; | ||
444 | |||
445 | s5k4aa_read_sensor(sd, address, &old_value, 1); | ||
446 | s5k4aa_write_sensor(sd, address, &test_value, 1); | ||
447 | s5k4aa_read_sensor(sd, address, &ctrl_value, 1); | ||
448 | |||
449 | if (ctrl_value == test_value) | ||
450 | info("register 0x%x is writeable", address); | ||
451 | else | ||
452 | info("register 0x%x is read only", address); | ||
453 | |||
454 | /* Restore original value */ | ||
455 | s5k4aa_write_sensor(sd, address, &old_value, 1); | ||
456 | } | ||
457 | } | ||
458 | info("Read/write register probing complete"); | ||
459 | s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); | ||
460 | } | ||