aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/gspca/m5602/m5602_s5k4aa.c')
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.c460
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
21int 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");
78sensor_found:
79 sd->gspca_dev.cam.cam_mode = s5k4aa.modes;
80 sd->gspca_dev.cam.nmodes = s5k4aa.nmodes;
81 return 0;
82}
83
84int 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 }
120out:
121 return (err < 0) ? err : 0;
122}
123
124int 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
168int 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
226int s5k4aa_power_down(struct sd *sd)
227{
228 return 0;
229}
230
231int 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);
249out:
250 return (err < 0) ? err : 0;
251}
252
253int 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);
269out:
270 return (err < 0) ? err : 0;
271}
272
273int 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
287out:
288 return (err < 0) ? err : 0;
289}
290
291int 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 }
325out:
326 return (err < 0) ? err : 0;
327}
328
329int 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);
342out:
343 return (err < 0) ? err : 0;
344}
345
346int 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 }
381out:
382 return (err < 0) ? err : 0;
383}
384
385int 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
399out:
400 return (err < 0) ? err : 0;
401}
402
403int 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
417out:
418 return (err < 0) ? err : 0;
419}
420
421void 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}