diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/video/ovcamchip/ovcamchip_core.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/video/ovcamchip/ovcamchip_core.c')
-rw-r--r-- | drivers/media/video/ovcamchip/ovcamchip_core.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c new file mode 100644 index 00000000000..54dd5612d3b --- /dev/null +++ b/drivers/media/video/ovcamchip/ovcamchip_core.c | |||
@@ -0,0 +1,444 @@ | |||
1 | /* Shared Code for OmniVision Camera Chip Drivers | ||
2 | * | ||
3 | * Copyright (c) 2004 Mark McClelland <mark@alpha.dyndns.org> | ||
4 | * http://alpha.dyndns.org/ov511/ | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. | ||
10 | */ | ||
11 | |||
12 | #define DEBUG | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/moduleparam.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include "ovcamchip_priv.h" | ||
20 | |||
21 | #define DRIVER_VERSION "v2.27 for Linux 2.6" | ||
22 | #define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org>" | ||
23 | #define DRIVER_DESC "OV camera chip I2C driver" | ||
24 | |||
25 | #define PINFO(fmt, args...) printk(KERN_INFO "ovcamchip: " fmt "\n" , ## args); | ||
26 | #define PERROR(fmt, args...) printk(KERN_ERR "ovcamchip: " fmt "\n" , ## args); | ||
27 | |||
28 | #ifdef DEBUG | ||
29 | int ovcamchip_debug = 0; | ||
30 | static int debug; | ||
31 | module_param(debug, int, 0); | ||
32 | MODULE_PARM_DESC(debug, | ||
33 | "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=all"); | ||
34 | #endif | ||
35 | |||
36 | /* By default, let bridge driver tell us if chip is monochrome. mono=0 | ||
37 | * will ignore that and always treat chips as color. mono=1 will force | ||
38 | * monochrome mode for all chips. */ | ||
39 | static int mono = -1; | ||
40 | module_param(mono, int, 0); | ||
41 | MODULE_PARM_DESC(mono, | ||
42 | "1=chips are monochrome (OVx1xx), 0=force color, -1=autodetect (default)"); | ||
43 | |||
44 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
45 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
46 | MODULE_LICENSE("GPL"); | ||
47 | |||
48 | /* Registers common to all chips, that are needed for detection */ | ||
49 | #define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ | ||
50 | #define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ | ||
51 | #define GENERIC_REG_COM_I 0x29 /* misc ID bits */ | ||
52 | |||
53 | extern struct ovcamchip_ops ov6x20_ops; | ||
54 | extern struct ovcamchip_ops ov6x30_ops; | ||
55 | extern struct ovcamchip_ops ov7x10_ops; | ||
56 | extern struct ovcamchip_ops ov7x20_ops; | ||
57 | extern struct ovcamchip_ops ov76be_ops; | ||
58 | |||
59 | static char *chip_names[NUM_CC_TYPES] = { | ||
60 | [CC_UNKNOWN] = "Unknown chip", | ||
61 | [CC_OV76BE] = "OV76BE", | ||
62 | [CC_OV7610] = "OV7610", | ||
63 | [CC_OV7620] = "OV7620", | ||
64 | [CC_OV7620AE] = "OV7620AE", | ||
65 | [CC_OV6620] = "OV6620", | ||
66 | [CC_OV6630] = "OV6630", | ||
67 | [CC_OV6630AE] = "OV6630AE", | ||
68 | [CC_OV6630AF] = "OV6630AF", | ||
69 | }; | ||
70 | |||
71 | /* Forward declarations */ | ||
72 | static struct i2c_driver driver; | ||
73 | static struct i2c_client client_template; | ||
74 | |||
75 | /* ----------------------------------------------------------------------- */ | ||
76 | |||
77 | int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals) | ||
78 | { | ||
79 | int rc; | ||
80 | |||
81 | while (rvals->reg != 0xff) { | ||
82 | rc = ov_write(c, rvals->reg, rvals->val); | ||
83 | if (rc < 0) | ||
84 | return rc; | ||
85 | rvals++; | ||
86 | } | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | /* Writes bits at positions specified by mask to an I2C reg. Bits that are in | ||
92 | * the same position as 1's in "mask" are cleared and set to "value". Bits | ||
93 | * that are in the same position as 0's in "mask" are preserved, regardless | ||
94 | * of their respective state in "value". | ||
95 | */ | ||
96 | int ov_write_mask(struct i2c_client *c, | ||
97 | unsigned char reg, | ||
98 | unsigned char value, | ||
99 | unsigned char mask) | ||
100 | { | ||
101 | int rc; | ||
102 | unsigned char oldval, newval; | ||
103 | |||
104 | if (mask == 0xff) { | ||
105 | newval = value; | ||
106 | } else { | ||
107 | rc = ov_read(c, reg, &oldval); | ||
108 | if (rc < 0) | ||
109 | return rc; | ||
110 | |||
111 | oldval &= (~mask); /* Clear the masked bits */ | ||
112 | value &= mask; /* Enforce mask on value */ | ||
113 | newval = oldval | value; /* Set the desired bits */ | ||
114 | } | ||
115 | |||
116 | return ov_write(c, reg, newval); | ||
117 | } | ||
118 | |||
119 | /* ----------------------------------------------------------------------- */ | ||
120 | |||
121 | /* Reset the chip and ensure that I2C is synchronized. Returns <0 if failure. | ||
122 | */ | ||
123 | static int init_camchip(struct i2c_client *c) | ||
124 | { | ||
125 | int i, success; | ||
126 | unsigned char high, low; | ||
127 | |||
128 | /* Reset the chip */ | ||
129 | ov_write(c, 0x12, 0x80); | ||
130 | |||
131 | /* Wait for it to initialize */ | ||
132 | msleep(150); | ||
133 | |||
134 | for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) { | ||
135 | if (ov_read(c, GENERIC_REG_ID_HIGH, &high) >= 0) { | ||
136 | if (ov_read(c, GENERIC_REG_ID_LOW, &low) >= 0) { | ||
137 | if (high == 0x7F && low == 0xA2) { | ||
138 | success = 1; | ||
139 | continue; | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | |||
144 | /* Reset the chip */ | ||
145 | ov_write(c, 0x12, 0x80); | ||
146 | |||
147 | /* Wait for it to initialize */ | ||
148 | msleep(150); | ||
149 | |||
150 | /* Dummy read to sync I2C */ | ||
151 | ov_read(c, 0x00, &low); | ||
152 | } | ||
153 | |||
154 | if (!success) | ||
155 | return -EIO; | ||
156 | |||
157 | PDEBUG(1, "I2C synced in %d attempt(s)", i); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | /* This detects the OV7610, OV7620, or OV76BE chip. */ | ||
163 | static int ov7xx0_detect(struct i2c_client *c) | ||
164 | { | ||
165 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
166 | int rc; | ||
167 | unsigned char val; | ||
168 | |||
169 | PDEBUG(4, ""); | ||
170 | |||
171 | /* Detect chip (sub)type */ | ||
172 | rc = ov_read(c, GENERIC_REG_COM_I, &val); | ||
173 | if (rc < 0) { | ||
174 | PERROR("Error detecting ov7xx0 type"); | ||
175 | return rc; | ||
176 | } | ||
177 | |||
178 | if ((val & 3) == 3) { | ||
179 | PINFO("Camera chip is an OV7610"); | ||
180 | ov->subtype = CC_OV7610; | ||
181 | } else if ((val & 3) == 1) { | ||
182 | rc = ov_read(c, 0x15, &val); | ||
183 | if (rc < 0) { | ||
184 | PERROR("Error detecting ov7xx0 type"); | ||
185 | return rc; | ||
186 | } | ||
187 | |||
188 | if (val & 1) { | ||
189 | PINFO("Camera chip is an OV7620AE"); | ||
190 | /* OV7620 is a close enough match for now. There are | ||
191 | * some definite differences though, so this should be | ||
192 | * fixed */ | ||
193 | ov->subtype = CC_OV7620; | ||
194 | } else { | ||
195 | PINFO("Camera chip is an OV76BE"); | ||
196 | ov->subtype = CC_OV76BE; | ||
197 | } | ||
198 | } else if ((val & 3) == 0) { | ||
199 | PINFO("Camera chip is an OV7620"); | ||
200 | ov->subtype = CC_OV7620; | ||
201 | } else { | ||
202 | PERROR("Unknown camera chip version: %d", val & 3); | ||
203 | return -ENOSYS; | ||
204 | } | ||
205 | |||
206 | if (ov->subtype == CC_OV76BE) | ||
207 | ov->sops = &ov76be_ops; | ||
208 | else if (ov->subtype == CC_OV7620) | ||
209 | ov->sops = &ov7x20_ops; | ||
210 | else | ||
211 | ov->sops = &ov7x10_ops; | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | /* This detects the OV6620, OV6630, OV6630AE, or OV6630AF chip. */ | ||
217 | static int ov6xx0_detect(struct i2c_client *c) | ||
218 | { | ||
219 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
220 | int rc; | ||
221 | unsigned char val; | ||
222 | |||
223 | PDEBUG(4, ""); | ||
224 | |||
225 | /* Detect chip (sub)type */ | ||
226 | rc = ov_read(c, GENERIC_REG_COM_I, &val); | ||
227 | if (rc < 0) { | ||
228 | PERROR("Error detecting ov6xx0 type"); | ||
229 | return -1; | ||
230 | } | ||
231 | |||
232 | if ((val & 3) == 0) { | ||
233 | ov->subtype = CC_OV6630; | ||
234 | PINFO("Camera chip is an OV6630"); | ||
235 | } else if ((val & 3) == 1) { | ||
236 | ov->subtype = CC_OV6620; | ||
237 | PINFO("Camera chip is an OV6620"); | ||
238 | } else if ((val & 3) == 2) { | ||
239 | ov->subtype = CC_OV6630; | ||
240 | PINFO("Camera chip is an OV6630AE"); | ||
241 | } else if ((val & 3) == 3) { | ||
242 | ov->subtype = CC_OV6630; | ||
243 | PINFO("Camera chip is an OV6630AF"); | ||
244 | } | ||
245 | |||
246 | if (ov->subtype == CC_OV6620) | ||
247 | ov->sops = &ov6x20_ops; | ||
248 | else | ||
249 | ov->sops = &ov6x30_ops; | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static int ovcamchip_detect(struct i2c_client *c) | ||
255 | { | ||
256 | /* Ideally we would just try a single register write and see if it NAKs. | ||
257 | * That isn't possible since the OV518 can't report I2C transaction | ||
258 | * failures. So, we have to try to initialize the chip (i.e. reset it | ||
259 | * and check the ID registers) to detect its presence. */ | ||
260 | |||
261 | /* Test for 7xx0 */ | ||
262 | PDEBUG(3, "Testing for 0V7xx0"); | ||
263 | c->addr = OV7xx0_SID; | ||
264 | if (init_camchip(c) < 0) { | ||
265 | /* Test for 6xx0 */ | ||
266 | PDEBUG(3, "Testing for 0V6xx0"); | ||
267 | c->addr = OV6xx0_SID; | ||
268 | if (init_camchip(c) < 0) { | ||
269 | return -ENODEV; | ||
270 | } else { | ||
271 | if (ov6xx0_detect(c) < 0) { | ||
272 | PERROR("Failed to init OV6xx0"); | ||
273 | return -EIO; | ||
274 | } | ||
275 | } | ||
276 | } else { | ||
277 | if (ov7xx0_detect(c) < 0) { | ||
278 | PERROR("Failed to init OV7xx0"); | ||
279 | return -EIO; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | /* ----------------------------------------------------------------------- */ | ||
287 | |||
288 | static int ovcamchip_attach(struct i2c_adapter *adap) | ||
289 | { | ||
290 | int rc = 0; | ||
291 | struct ovcamchip *ov; | ||
292 | struct i2c_client *c; | ||
293 | |||
294 | /* I2C is not a PnP bus, so we can never be certain that we're talking | ||
295 | * to the right chip. To prevent damage to EEPROMS and such, only | ||
296 | * attach to adapters that are known to contain OV camera chips. */ | ||
297 | |||
298 | switch (adap->id) { | ||
299 | case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV511): | ||
300 | case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518): | ||
301 | case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OVFX2): | ||
302 | case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_W9968CF): | ||
303 | PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id); | ||
304 | break; | ||
305 | default: | ||
306 | PDEBUG(1, "Adapter ID 0x%06x rejected", adap->id); | ||
307 | return -ENODEV; | ||
308 | } | ||
309 | |||
310 | c = kmalloc(sizeof *c, GFP_KERNEL); | ||
311 | if (!c) { | ||
312 | rc = -ENOMEM; | ||
313 | goto no_client; | ||
314 | } | ||
315 | memcpy(c, &client_template, sizeof *c); | ||
316 | c->adapter = adap; | ||
317 | strcpy(i2c_clientname(c), "OV????"); | ||
318 | |||
319 | ov = kmalloc(sizeof *ov, GFP_KERNEL); | ||
320 | if (!ov) { | ||
321 | rc = -ENOMEM; | ||
322 | goto no_ov; | ||
323 | } | ||
324 | memset(ov, 0, sizeof *ov); | ||
325 | i2c_set_clientdata(c, ov); | ||
326 | |||
327 | rc = ovcamchip_detect(c); | ||
328 | if (rc < 0) | ||
329 | goto error; | ||
330 | |||
331 | strcpy(i2c_clientname(c), chip_names[ov->subtype]); | ||
332 | |||
333 | PDEBUG(1, "Camera chip detection complete"); | ||
334 | |||
335 | i2c_attach_client(c); | ||
336 | |||
337 | return rc; | ||
338 | error: | ||
339 | kfree(ov); | ||
340 | no_ov: | ||
341 | kfree(c); | ||
342 | no_client: | ||
343 | PDEBUG(1, "returning %d", rc); | ||
344 | return rc; | ||
345 | } | ||
346 | |||
347 | static int ovcamchip_detach(struct i2c_client *c) | ||
348 | { | ||
349 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
350 | int rc; | ||
351 | |||
352 | rc = ov->sops->free(c); | ||
353 | if (rc < 0) | ||
354 | return rc; | ||
355 | |||
356 | i2c_detach_client(c); | ||
357 | |||
358 | kfree(ov); | ||
359 | kfree(c); | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) | ||
364 | { | ||
365 | struct ovcamchip *ov = i2c_get_clientdata(c); | ||
366 | |||
367 | if (!ov->initialized && | ||
368 | cmd != OVCAMCHIP_CMD_Q_SUBTYPE && | ||
369 | cmd != OVCAMCHIP_CMD_INITIALIZE) { | ||
370 | dev_err(&c->dev, "ERROR: Camera chip not initialized yet!\n"); | ||
371 | return -EPERM; | ||
372 | } | ||
373 | |||
374 | switch (cmd) { | ||
375 | case OVCAMCHIP_CMD_Q_SUBTYPE: | ||
376 | { | ||
377 | *(int *)arg = ov->subtype; | ||
378 | return 0; | ||
379 | } | ||
380 | case OVCAMCHIP_CMD_INITIALIZE: | ||
381 | { | ||
382 | int rc; | ||
383 | |||
384 | if (mono == -1) | ||
385 | ov->mono = *(int *)arg; | ||
386 | else | ||
387 | ov->mono = mono; | ||
388 | |||
389 | if (ov->mono) { | ||
390 | if (ov->subtype != CC_OV7620) | ||
391 | dev_warn(&c->dev, "Warning: Monochrome not " | ||
392 | "implemented for this chip\n"); | ||
393 | else | ||
394 | dev_info(&c->dev, "Initializing chip as " | ||
395 | "monochrome\n"); | ||
396 | } | ||
397 | |||
398 | rc = ov->sops->init(c); | ||
399 | if (rc < 0) | ||
400 | return rc; | ||
401 | |||
402 | ov->initialized = 1; | ||
403 | return 0; | ||
404 | } | ||
405 | default: | ||
406 | return ov->sops->command(c, cmd, arg); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | /* ----------------------------------------------------------------------- */ | ||
411 | |||
412 | static struct i2c_driver driver = { | ||
413 | .owner = THIS_MODULE, | ||
414 | .name = "ovcamchip", | ||
415 | .id = I2C_DRIVERID_OVCAMCHIP, | ||
416 | .class = I2C_CLASS_CAM_DIGITAL, | ||
417 | .flags = I2C_DF_NOTIFY, | ||
418 | .attach_adapter = ovcamchip_attach, | ||
419 | .detach_client = ovcamchip_detach, | ||
420 | .command = ovcamchip_command, | ||
421 | }; | ||
422 | |||
423 | static struct i2c_client client_template = { | ||
424 | I2C_DEVNAME("(unset)"), | ||
425 | .driver = &driver, | ||
426 | }; | ||
427 | |||
428 | static int __init ovcamchip_init(void) | ||
429 | { | ||
430 | #ifdef DEBUG | ||
431 | ovcamchip_debug = debug; | ||
432 | #endif | ||
433 | |||
434 | PINFO(DRIVER_VERSION " : " DRIVER_DESC); | ||
435 | return i2c_add_driver(&driver); | ||
436 | } | ||
437 | |||
438 | static void __exit ovcamchip_exit(void) | ||
439 | { | ||
440 | i2c_del_driver(&driver); | ||
441 | } | ||
442 | |||
443 | module_init(ovcamchip_init); | ||
444 | module_exit(ovcamchip_exit); | ||