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/w9966.c |
Linux-2.6.12-rc2v2.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/w9966.c')
-rw-r--r-- | drivers/media/video/w9966.c | 984 |
1 files changed, 984 insertions, 0 deletions
diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c new file mode 100644 index 000000000000..c318ba32fbaf --- /dev/null +++ b/drivers/media/video/w9966.c | |||
@@ -0,0 +1,984 @@ | |||
1 | /* | ||
2 | Winbond w9966cf Webcam parport driver. | ||
3 | |||
4 | Version 0.32 | ||
5 | |||
6 | Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se> | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2 of the License, or | ||
11 | (at your option) any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; if not, write to the Free Software | ||
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | /* | ||
23 | Supported devices: | ||
24 | *Lifeview FlyCam Supra (using the Philips saa7111a chip) | ||
25 | |||
26 | Does any other model using the w9966 interface chip exist ? | ||
27 | |||
28 | Todo: | ||
29 | |||
30 | *Add a working EPP mode, since DMA ECP read isn't implemented | ||
31 | in the parport drivers. (That's why it's so sloow) | ||
32 | |||
33 | *Add support for other ccd-control chips than the saa7111 | ||
34 | please send me feedback on what kind of chips you have. | ||
35 | |||
36 | *Add proper probing. I don't know what's wrong with the IEEE1284 | ||
37 | parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID) | ||
38 | and nibble read seems to be broken for some peripherals. | ||
39 | |||
40 | *Add probing for onboard SRAM, port directions etc. (if possible) | ||
41 | |||
42 | *Add support for the hardware compressed modes (maybe using v4l2) | ||
43 | |||
44 | *Fix better support for the capture window (no skewed images, v4l | ||
45 | interface to capt. window) | ||
46 | |||
47 | *Probably some bugs that I don't know of | ||
48 | |||
49 | Please support me by sending feedback! | ||
50 | |||
51 | Changes: | ||
52 | |||
53 | Alan Cox: Removed RGB mode for kernel merge, added THIS_MODULE | ||
54 | and owner support for newer module locks | ||
55 | */ | ||
56 | |||
57 | #include <linux/module.h> | ||
58 | #include <linux/init.h> | ||
59 | #include <linux/delay.h> | ||
60 | #include <linux/videodev.h> | ||
61 | #include <linux/parport.h> | ||
62 | |||
63 | //#define DEBUG // Undef me for production | ||
64 | |||
65 | #ifdef DEBUG | ||
66 | #define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __FUNCTION__ , ##a) | ||
67 | #else | ||
68 | #define DPRINTF(x...) | ||
69 | #endif | ||
70 | |||
71 | /* | ||
72 | * Defines, simple typedefs etc. | ||
73 | */ | ||
74 | |||
75 | #define W9966_DRIVERNAME "W9966CF Webcam" | ||
76 | #define W9966_MAXCAMS 4 // Maximum number of cameras | ||
77 | #define W9966_RBUFFER 2048 // Read buffer (must be an even number) | ||
78 | #define W9966_SRAMSIZE 131072 // 128kb | ||
79 | #define W9966_SRAMID 0x02 // check w9966cf.pdf | ||
80 | |||
81 | // Empirically determined window limits | ||
82 | #define W9966_WND_MIN_X 16 | ||
83 | #define W9966_WND_MIN_Y 14 | ||
84 | #define W9966_WND_MAX_X 705 | ||
85 | #define W9966_WND_MAX_Y 253 | ||
86 | #define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X) | ||
87 | #define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y) | ||
88 | |||
89 | // Keep track of our current state | ||
90 | #define W9966_STATE_PDEV 0x01 | ||
91 | #define W9966_STATE_CLAIMED 0x02 | ||
92 | #define W9966_STATE_VDEV 0x04 | ||
93 | |||
94 | #define W9966_I2C_W_ID 0x48 | ||
95 | #define W9966_I2C_R_ID 0x49 | ||
96 | #define W9966_I2C_R_DATA 0x08 | ||
97 | #define W9966_I2C_R_CLOCK 0x04 | ||
98 | #define W9966_I2C_W_DATA 0x02 | ||
99 | #define W9966_I2C_W_CLOCK 0x01 | ||
100 | |||
101 | struct w9966_dev { | ||
102 | unsigned char dev_state; | ||
103 | unsigned char i2c_state; | ||
104 | unsigned short ppmode; | ||
105 | struct parport* pport; | ||
106 | struct pardevice* pdev; | ||
107 | struct video_device vdev; | ||
108 | unsigned short width; | ||
109 | unsigned short height; | ||
110 | unsigned char brightness; | ||
111 | signed char contrast; | ||
112 | signed char color; | ||
113 | signed char hue; | ||
114 | }; | ||
115 | |||
116 | /* | ||
117 | * Module specific properties | ||
118 | */ | ||
119 | |||
120 | MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>"); | ||
121 | MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)"); | ||
122 | MODULE_LICENSE("GPL"); | ||
123 | |||
124 | |||
125 | #ifdef MODULE | ||
126 | static const char* pardev[] = {[0 ... W9966_MAXCAMS] = ""}; | ||
127 | #else | ||
128 | static const char* pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; | ||
129 | #endif | ||
130 | module_param_array(pardev, charp, NULL, 0); | ||
131 | MODULE_PARM_DESC(pardev, "pardev: where to search for\n\ | ||
132 | \teach camera. 'aggressive' means brute-force search.\n\ | ||
133 | \tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n\ | ||
134 | \tcam 1 to parport3 and search every parport for cam 2 etc..."); | ||
135 | |||
136 | static int parmode = 0; | ||
137 | module_param(parmode, int, 0); | ||
138 | MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp"); | ||
139 | |||
140 | static int video_nr = -1; | ||
141 | module_param(video_nr, int, 0); | ||
142 | |||
143 | /* | ||
144 | * Private data | ||
145 | */ | ||
146 | |||
147 | static struct w9966_dev w9966_cams[W9966_MAXCAMS]; | ||
148 | |||
149 | /* | ||
150 | * Private function declares | ||
151 | */ | ||
152 | |||
153 | static inline void w9966_setState(struct w9966_dev* cam, int mask, int val); | ||
154 | static inline int w9966_getState(struct w9966_dev* cam, int mask, int val); | ||
155 | static inline void w9966_pdev_claim(struct w9966_dev *vdev); | ||
156 | static inline void w9966_pdev_release(struct w9966_dev *vdev); | ||
157 | |||
158 | static int w9966_rReg(struct w9966_dev* cam, int reg); | ||
159 | static int w9966_wReg(struct w9966_dev* cam, int reg, int data); | ||
160 | #if 0 | ||
161 | static int w9966_rReg_i2c(struct w9966_dev* cam, int reg); | ||
162 | #endif | ||
163 | static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data); | ||
164 | static int w9966_findlen(int near, int size, int maxlen); | ||
165 | static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor); | ||
166 | static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h); | ||
167 | |||
168 | static int w9966_init(struct w9966_dev* cam, struct parport* port); | ||
169 | static void w9966_term(struct w9966_dev* cam); | ||
170 | |||
171 | static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state); | ||
172 | static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state); | ||
173 | static inline int w9966_i2c_getsda(struct w9966_dev* cam); | ||
174 | static inline int w9966_i2c_getscl(struct w9966_dev* cam); | ||
175 | static int w9966_i2c_wbyte(struct w9966_dev* cam, int data); | ||
176 | #if 0 | ||
177 | static int w9966_i2c_rbyte(struct w9966_dev* cam); | ||
178 | #endif | ||
179 | |||
180 | static int w9966_v4l_ioctl(struct inode *inode, struct file *file, | ||
181 | unsigned int cmd, unsigned long arg); | ||
182 | static ssize_t w9966_v4l_read(struct file *file, char __user *buf, | ||
183 | size_t count, loff_t *ppos); | ||
184 | |||
185 | static struct file_operations w9966_fops = { | ||
186 | .owner = THIS_MODULE, | ||
187 | .open = video_exclusive_open, | ||
188 | .release = video_exclusive_release, | ||
189 | .ioctl = w9966_v4l_ioctl, | ||
190 | .read = w9966_v4l_read, | ||
191 | .llseek = no_llseek, | ||
192 | }; | ||
193 | static struct video_device w9966_template = { | ||
194 | .owner = THIS_MODULE, | ||
195 | .name = W9966_DRIVERNAME, | ||
196 | .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, | ||
197 | .hardware = VID_HARDWARE_W9966, | ||
198 | .fops = &w9966_fops, | ||
199 | }; | ||
200 | |||
201 | /* | ||
202 | * Private function defines | ||
203 | */ | ||
204 | |||
205 | |||
206 | // Set camera phase flags, so we know what to uninit when terminating | ||
207 | static inline void w9966_setState(struct w9966_dev* cam, int mask, int val) | ||
208 | { | ||
209 | cam->dev_state = (cam->dev_state & ~mask) ^ val; | ||
210 | } | ||
211 | |||
212 | // Get camera phase flags | ||
213 | static inline int w9966_getState(struct w9966_dev* cam, int mask, int val) | ||
214 | { | ||
215 | return ((cam->dev_state & mask) == val); | ||
216 | } | ||
217 | |||
218 | // Claim parport for ourself | ||
219 | static inline void w9966_pdev_claim(struct w9966_dev* cam) | ||
220 | { | ||
221 | if (w9966_getState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED)) | ||
222 | return; | ||
223 | parport_claim_or_block(cam->pdev); | ||
224 | w9966_setState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED); | ||
225 | } | ||
226 | |||
227 | // Release parport for others to use | ||
228 | static inline void w9966_pdev_release(struct w9966_dev* cam) | ||
229 | { | ||
230 | if (w9966_getState(cam, W9966_STATE_CLAIMED, 0)) | ||
231 | return; | ||
232 | parport_release(cam->pdev); | ||
233 | w9966_setState(cam, W9966_STATE_CLAIMED, 0); | ||
234 | } | ||
235 | |||
236 | // Read register from W9966 interface-chip | ||
237 | // Expects a claimed pdev | ||
238 | // -1 on error, else register data (byte) | ||
239 | static int w9966_rReg(struct w9966_dev* cam, int reg) | ||
240 | { | ||
241 | // ECP, read, regtransfer, REG, REG, REG, REG, REG | ||
242 | const unsigned char addr = 0x80 | (reg & 0x1f); | ||
243 | unsigned char val; | ||
244 | |||
245 | if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0) | ||
246 | return -1; | ||
247 | if (parport_write(cam->pport, &addr, 1) != 1) | ||
248 | return -1; | ||
249 | if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0) | ||
250 | return -1; | ||
251 | if (parport_read(cam->pport, &val, 1) != 1) | ||
252 | return -1; | ||
253 | |||
254 | return val; | ||
255 | } | ||
256 | |||
257 | // Write register to W9966 interface-chip | ||
258 | // Expects a claimed pdev | ||
259 | // -1 on error | ||
260 | static int w9966_wReg(struct w9966_dev* cam, int reg, int data) | ||
261 | { | ||
262 | // ECP, write, regtransfer, REG, REG, REG, REG, REG | ||
263 | const unsigned char addr = 0xc0 | (reg & 0x1f); | ||
264 | const unsigned char val = data; | ||
265 | |||
266 | if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0) | ||
267 | return -1; | ||
268 | if (parport_write(cam->pport, &addr, 1) != 1) | ||
269 | return -1; | ||
270 | if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0) | ||
271 | return -1; | ||
272 | if (parport_write(cam->pport, &val, 1) != 1) | ||
273 | return -1; | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | // Initialize camera device. Setup all internal flags, set a | ||
279 | // default video mode, setup ccd-chip, register v4l device etc.. | ||
280 | // Also used for 'probing' of hardware. | ||
281 | // -1 on error | ||
282 | static int w9966_init(struct w9966_dev* cam, struct parport* port) | ||
283 | { | ||
284 | if (cam->dev_state != 0) | ||
285 | return -1; | ||
286 | |||
287 | cam->pport = port; | ||
288 | cam->brightness = 128; | ||
289 | cam->contrast = 64; | ||
290 | cam->color = 64; | ||
291 | cam->hue = 0; | ||
292 | |||
293 | // Select requested transfer mode | ||
294 | switch(parmode) | ||
295 | { | ||
296 | default: // Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) | ||
297 | case 0: | ||
298 | if (port->modes & PARPORT_MODE_ECP) | ||
299 | cam->ppmode = IEEE1284_MODE_ECP; | ||
300 | else if (port->modes & PARPORT_MODE_EPP) | ||
301 | cam->ppmode = IEEE1284_MODE_EPP; | ||
302 | else | ||
303 | cam->ppmode = IEEE1284_MODE_ECP; | ||
304 | break; | ||
305 | case 1: // hw- or sw-ecp | ||
306 | cam->ppmode = IEEE1284_MODE_ECP; | ||
307 | break; | ||
308 | case 2: // hw- or sw-epp | ||
309 | cam->ppmode = IEEE1284_MODE_EPP; | ||
310 | break; | ||
311 | } | ||
312 | |||
313 | // Tell the parport driver that we exists | ||
314 | cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); | ||
315 | if (cam->pdev == NULL) { | ||
316 | DPRINTF("parport_register_device() failed\n"); | ||
317 | return -1; | ||
318 | } | ||
319 | w9966_setState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); | ||
320 | |||
321 | w9966_pdev_claim(cam); | ||
322 | |||
323 | // Setup a default capture mode | ||
324 | if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { | ||
325 | DPRINTF("w9966_setup() failed.\n"); | ||
326 | return -1; | ||
327 | } | ||
328 | |||
329 | w9966_pdev_release(cam); | ||
330 | |||
331 | // Fill in the video_device struct and register us to v4l | ||
332 | memcpy(&cam->vdev, &w9966_template, sizeof(struct video_device)); | ||
333 | cam->vdev.priv = cam; | ||
334 | |||
335 | if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) | ||
336 | return -1; | ||
337 | |||
338 | w9966_setState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); | ||
339 | |||
340 | // All ok | ||
341 | printk( | ||
342 | "w9966cf: Found and initialized a webcam on %s.\n", | ||
343 | cam->pport->name | ||
344 | ); | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | |||
349 | // Terminate everything gracefully | ||
350 | static void w9966_term(struct w9966_dev* cam) | ||
351 | { | ||
352 | // Unregister from v4l | ||
353 | if (w9966_getState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) { | ||
354 | video_unregister_device(&cam->vdev); | ||
355 | w9966_setState(cam, W9966_STATE_VDEV, 0); | ||
356 | } | ||
357 | |||
358 | // Terminate from IEEE1284 mode and release pdev block | ||
359 | if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { | ||
360 | w9966_pdev_claim(cam); | ||
361 | parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT); | ||
362 | w9966_pdev_release(cam); | ||
363 | } | ||
364 | |||
365 | // Unregister from parport | ||
366 | if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { | ||
367 | parport_unregister_device(cam->pdev); | ||
368 | w9966_setState(cam, W9966_STATE_PDEV, 0); | ||
369 | } | ||
370 | } | ||
371 | |||
372 | |||
373 | // Find a good length for capture window (used both for W and H) | ||
374 | // A bit ugly but pretty functional. The capture length | ||
375 | // have to match the downscale | ||
376 | static int w9966_findlen(int near, int size, int maxlen) | ||
377 | { | ||
378 | int bestlen = size; | ||
379 | int besterr = abs(near - bestlen); | ||
380 | int len; | ||
381 | |||
382 | for(len = size+1;len < maxlen;len++) | ||
383 | { | ||
384 | int err; | ||
385 | if ( ((64*size) %len) != 0) | ||
386 | continue; | ||
387 | |||
388 | err = abs(near - len); | ||
389 | |||
390 | // Only continue as long as we keep getting better values | ||
391 | if (err > besterr) | ||
392 | break; | ||
393 | |||
394 | besterr = err; | ||
395 | bestlen = len; | ||
396 | } | ||
397 | |||
398 | return bestlen; | ||
399 | } | ||
400 | |||
401 | // Modify capture window (if necessary) | ||
402 | // and calculate downscaling | ||
403 | // Return -1 on error | ||
404 | static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor) | ||
405 | { | ||
406 | int maxlen = max - min; | ||
407 | int len = *end - *beg + 1; | ||
408 | int newlen = w9966_findlen(len, size, maxlen); | ||
409 | int err = newlen - len; | ||
410 | |||
411 | // Check for bad format | ||
412 | if (newlen > maxlen || newlen < size) | ||
413 | return -1; | ||
414 | |||
415 | // Set factor (6 bit fixed) | ||
416 | *factor = (64*size) / newlen; | ||
417 | if (*factor == 64) | ||
418 | *factor = 0x00; // downscale is disabled | ||
419 | else | ||
420 | *factor |= 0x80; // set downscale-enable bit | ||
421 | |||
422 | // Modify old beginning and end | ||
423 | *beg -= err / 2; | ||
424 | *end += err - (err / 2); | ||
425 | |||
426 | // Move window if outside borders | ||
427 | if (*beg < min) { | ||
428 | *end += min - *beg; | ||
429 | *beg += min - *beg; | ||
430 | } | ||
431 | if (*end > max) { | ||
432 | *beg -= *end - max; | ||
433 | *end -= *end - max; | ||
434 | } | ||
435 | |||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | // Setup the cameras capture window etc. | ||
440 | // Expects a claimed pdev | ||
441 | // return -1 on error | ||
442 | static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h) | ||
443 | { | ||
444 | unsigned int i; | ||
445 | unsigned int enh_s, enh_e; | ||
446 | unsigned char scale_x, scale_y; | ||
447 | unsigned char regs[0x1c]; | ||
448 | unsigned char saa7111_regs[] = { | ||
449 | 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00, | ||
450 | 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00, | ||
451 | 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
452 | 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0 | ||
453 | }; | ||
454 | |||
455 | |||
456 | if (w*h*2 > W9966_SRAMSIZE) | ||
457 | { | ||
458 | DPRINTF("capture window exceeds SRAM size!.\n"); | ||
459 | w = 200; h = 160; // Pick default values | ||
460 | } | ||
461 | |||
462 | w &= ~0x1; | ||
463 | if (w < 2) w = 2; | ||
464 | if (h < 1) h = 1; | ||
465 | if (w > W9966_WND_MAX_W) w = W9966_WND_MAX_W; | ||
466 | if (h > W9966_WND_MAX_H) h = W9966_WND_MAX_H; | ||
467 | |||
468 | cam->width = w; | ||
469 | cam->height = h; | ||
470 | |||
471 | enh_s = 0; | ||
472 | enh_e = w*h*2; | ||
473 | |||
474 | // Modify capture window if necessary and calculate downscaling | ||
475 | if ( | ||
476 | w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || | ||
477 | w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0 | ||
478 | ) return -1; | ||
479 | |||
480 | DPRINTF( | ||
481 | "%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", | ||
482 | w, h, x1, x2, y1, y2, scale_x&~0x80, scale_y&~0x80 | ||
483 | ); | ||
484 | |||
485 | // Setup registers | ||
486 | regs[0x00] = 0x00; // Set normal operation | ||
487 | regs[0x01] = 0x18; // Capture mode | ||
488 | regs[0x02] = scale_y; // V-scaling | ||
489 | regs[0x03] = scale_x; // H-scaling | ||
490 | |||
491 | // Capture window | ||
492 | regs[0x04] = (x1 & 0x0ff); // X-start (8 low bits) | ||
493 | regs[0x05] = (x1 & 0x300)>>8; // X-start (2 high bits) | ||
494 | regs[0x06] = (y1 & 0x0ff); // Y-start (8 low bits) | ||
495 | regs[0x07] = (y1 & 0x300)>>8; // Y-start (2 high bits) | ||
496 | regs[0x08] = (x2 & 0x0ff); // X-end (8 low bits) | ||
497 | regs[0x09] = (x2 & 0x300)>>8; // X-end (2 high bits) | ||
498 | regs[0x0a] = (y2 & 0x0ff); // Y-end (8 low bits) | ||
499 | |||
500 | regs[0x0c] = W9966_SRAMID; // SRAM-banks (1x 128kb) | ||
501 | |||
502 | // Enhancement layer | ||
503 | regs[0x0d] = (enh_s& 0x000ff); // Enh. start (0-7) | ||
504 | regs[0x0e] = (enh_s& 0x0ff00)>>8; // Enh. start (8-15) | ||
505 | regs[0x0f] = (enh_s& 0x70000)>>16; // Enh. start (16-17/18??) | ||
506 | regs[0x10] = (enh_e& 0x000ff); // Enh. end (0-7) | ||
507 | regs[0x11] = (enh_e& 0x0ff00)>>8; // Enh. end (8-15) | ||
508 | regs[0x12] = (enh_e& 0x70000)>>16; // Enh. end (16-17/18??) | ||
509 | |||
510 | // Misc | ||
511 | regs[0x13] = 0x40; // VEE control (raw 4:2:2) | ||
512 | regs[0x17] = 0x00; // ??? | ||
513 | regs[0x18] = cam->i2c_state = 0x00; // Serial bus | ||
514 | regs[0x19] = 0xff; // I/O port direction control | ||
515 | regs[0x1a] = 0xff; // I/O port data register | ||
516 | regs[0x1b] = 0x10; // ??? | ||
517 | |||
518 | // SAA7111 chip settings | ||
519 | saa7111_regs[0x0a] = cam->brightness; | ||
520 | saa7111_regs[0x0b] = cam->contrast; | ||
521 | saa7111_regs[0x0c] = cam->color; | ||
522 | saa7111_regs[0x0d] = cam->hue; | ||
523 | |||
524 | // Reset (ECP-fifo & serial-bus) | ||
525 | if (w9966_wReg(cam, 0x00, 0x03) == -1) | ||
526 | return -1; | ||
527 | |||
528 | // Write regs to w9966cf chip | ||
529 | for (i = 0; i < 0x1c; i++) | ||
530 | if (w9966_wReg(cam, i, regs[i]) == -1) | ||
531 | return -1; | ||
532 | |||
533 | // Write regs to saa7111 chip | ||
534 | for (i = 0; i < 0x20; i++) | ||
535 | if (w9966_wReg_i2c(cam, i, saa7111_regs[i]) == -1) | ||
536 | return -1; | ||
537 | |||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | /* | ||
542 | * Ugly and primitive i2c protocol functions | ||
543 | */ | ||
544 | |||
545 | // Sets the data line on the i2c bus. | ||
546 | // Expects a claimed pdev. | ||
547 | static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state) | ||
548 | { | ||
549 | if (state) | ||
550 | cam->i2c_state |= W9966_I2C_W_DATA; | ||
551 | else | ||
552 | cam->i2c_state &= ~W9966_I2C_W_DATA; | ||
553 | |||
554 | w9966_wReg(cam, 0x18, cam->i2c_state); | ||
555 | udelay(5); | ||
556 | } | ||
557 | |||
558 | // Get peripheral clock line | ||
559 | // Expects a claimed pdev. | ||
560 | static inline int w9966_i2c_getscl(struct w9966_dev* cam) | ||
561 | { | ||
562 | const unsigned char state = w9966_rReg(cam, 0x18); | ||
563 | return ((state & W9966_I2C_R_CLOCK) > 0); | ||
564 | } | ||
565 | |||
566 | // Sets the clock line on the i2c bus. | ||
567 | // Expects a claimed pdev. -1 on error | ||
568 | static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state) | ||
569 | { | ||
570 | unsigned long timeout; | ||
571 | |||
572 | if (state) | ||
573 | cam->i2c_state |= W9966_I2C_W_CLOCK; | ||
574 | else | ||
575 | cam->i2c_state &= ~W9966_I2C_W_CLOCK; | ||
576 | |||
577 | w9966_wReg(cam, 0x18, cam->i2c_state); | ||
578 | udelay(5); | ||
579 | |||
580 | // we go to high, we also expect the peripheral to ack. | ||
581 | if (state) { | ||
582 | timeout = jiffies + 100; | ||
583 | while (!w9966_i2c_getscl(cam)) { | ||
584 | if (time_after(jiffies, timeout)) | ||
585 | return -1; | ||
586 | } | ||
587 | } | ||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | // Get peripheral data line | ||
592 | // Expects a claimed pdev. | ||
593 | static inline int w9966_i2c_getsda(struct w9966_dev* cam) | ||
594 | { | ||
595 | const unsigned char state = w9966_rReg(cam, 0x18); | ||
596 | return ((state & W9966_I2C_R_DATA) > 0); | ||
597 | } | ||
598 | |||
599 | // Write a byte with ack to the i2c bus. | ||
600 | // Expects a claimed pdev. -1 on error | ||
601 | static int w9966_i2c_wbyte(struct w9966_dev* cam, int data) | ||
602 | { | ||
603 | int i; | ||
604 | for (i = 7; i >= 0; i--) | ||
605 | { | ||
606 | w9966_i2c_setsda(cam, (data >> i) & 0x01); | ||
607 | |||
608 | if (w9966_i2c_setscl(cam, 1) == -1) | ||
609 | return -1; | ||
610 | w9966_i2c_setscl(cam, 0); | ||
611 | } | ||
612 | |||
613 | w9966_i2c_setsda(cam, 1); | ||
614 | |||
615 | if (w9966_i2c_setscl(cam, 1) == -1) | ||
616 | return -1; | ||
617 | w9966_i2c_setscl(cam, 0); | ||
618 | |||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | // Read a data byte with ack from the i2c-bus | ||
623 | // Expects a claimed pdev. -1 on error | ||
624 | #if 0 | ||
625 | static int w9966_i2c_rbyte(struct w9966_dev* cam) | ||
626 | { | ||
627 | unsigned char data = 0x00; | ||
628 | int i; | ||
629 | |||
630 | w9966_i2c_setsda(cam, 1); | ||
631 | |||
632 | for (i = 0; i < 8; i++) | ||
633 | { | ||
634 | if (w9966_i2c_setscl(cam, 1) == -1) | ||
635 | return -1; | ||
636 | data = data << 1; | ||
637 | if (w9966_i2c_getsda(cam)) | ||
638 | data |= 0x01; | ||
639 | |||
640 | w9966_i2c_setscl(cam, 0); | ||
641 | } | ||
642 | return data; | ||
643 | } | ||
644 | #endif | ||
645 | |||
646 | // Read a register from the i2c device. | ||
647 | // Expects claimed pdev. -1 on error | ||
648 | #if 0 | ||
649 | static int w9966_rReg_i2c(struct w9966_dev* cam, int reg) | ||
650 | { | ||
651 | int data; | ||
652 | |||
653 | w9966_i2c_setsda(cam, 0); | ||
654 | w9966_i2c_setscl(cam, 0); | ||
655 | |||
656 | if ( | ||
657 | w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || | ||
658 | w9966_i2c_wbyte(cam, reg) == -1 | ||
659 | ) | ||
660 | return -1; | ||
661 | |||
662 | w9966_i2c_setsda(cam, 1); | ||
663 | if (w9966_i2c_setscl(cam, 1) == -1) | ||
664 | return -1; | ||
665 | w9966_i2c_setsda(cam, 0); | ||
666 | w9966_i2c_setscl(cam, 0); | ||
667 | |||
668 | if ( | ||
669 | w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1 || | ||
670 | (data = w9966_i2c_rbyte(cam)) == -1 | ||
671 | ) | ||
672 | return -1; | ||
673 | |||
674 | w9966_i2c_setsda(cam, 0); | ||
675 | |||
676 | if (w9966_i2c_setscl(cam, 1) == -1) | ||
677 | return -1; | ||
678 | w9966_i2c_setsda(cam, 1); | ||
679 | |||
680 | return data; | ||
681 | } | ||
682 | #endif | ||
683 | |||
684 | // Write a register to the i2c device. | ||
685 | // Expects claimed pdev. -1 on error | ||
686 | static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data) | ||
687 | { | ||
688 | w9966_i2c_setsda(cam, 0); | ||
689 | w9966_i2c_setscl(cam, 0); | ||
690 | |||
691 | if ( | ||
692 | w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || | ||
693 | w9966_i2c_wbyte(cam, reg) == -1 || | ||
694 | w9966_i2c_wbyte(cam, data) == -1 | ||
695 | ) | ||
696 | return -1; | ||
697 | |||
698 | w9966_i2c_setsda(cam, 0); | ||
699 | if (w9966_i2c_setscl(cam, 1) == -1) | ||
700 | return -1; | ||
701 | |||
702 | w9966_i2c_setsda(cam, 1); | ||
703 | |||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | /* | ||
708 | * Video4linux interfacing | ||
709 | */ | ||
710 | |||
711 | static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file, | ||
712 | unsigned int cmd, void *arg) | ||
713 | { | ||
714 | struct video_device *vdev = video_devdata(file); | ||
715 | struct w9966_dev *cam = vdev->priv; | ||
716 | |||
717 | switch(cmd) | ||
718 | { | ||
719 | case VIDIOCGCAP: | ||
720 | { | ||
721 | static struct video_capability vcap = { | ||
722 | .name = W9966_DRIVERNAME, | ||
723 | .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, | ||
724 | .channels = 1, | ||
725 | .maxwidth = W9966_WND_MAX_W, | ||
726 | .maxheight = W9966_WND_MAX_H, | ||
727 | .minwidth = 2, | ||
728 | .minheight = 1, | ||
729 | }; | ||
730 | struct video_capability *cap = arg; | ||
731 | *cap = vcap; | ||
732 | return 0; | ||
733 | } | ||
734 | case VIDIOCGCHAN: | ||
735 | { | ||
736 | struct video_channel *vch = arg; | ||
737 | if(vch->channel != 0) // We only support one channel (#0) | ||
738 | return -EINVAL; | ||
739 | memset(vch,0,sizeof(*vch)); | ||
740 | strcpy(vch->name, "CCD-input"); | ||
741 | vch->type = VIDEO_TYPE_CAMERA; | ||
742 | return 0; | ||
743 | } | ||
744 | case VIDIOCSCHAN: | ||
745 | { | ||
746 | struct video_channel *vch = arg; | ||
747 | if(vch->channel != 0) | ||
748 | return -EINVAL; | ||
749 | return 0; | ||
750 | } | ||
751 | case VIDIOCGTUNER: | ||
752 | { | ||
753 | struct video_tuner *vtune = arg; | ||
754 | if(vtune->tuner != 0) | ||
755 | return -EINVAL; | ||
756 | strcpy(vtune->name, "no tuner"); | ||
757 | vtune->rangelow = 0; | ||
758 | vtune->rangehigh = 0; | ||
759 | vtune->flags = VIDEO_TUNER_NORM; | ||
760 | vtune->mode = VIDEO_MODE_AUTO; | ||
761 | vtune->signal = 0xffff; | ||
762 | return 0; | ||
763 | } | ||
764 | case VIDIOCSTUNER: | ||
765 | { | ||
766 | struct video_tuner *vtune = arg; | ||
767 | if (vtune->tuner != 0) | ||
768 | return -EINVAL; | ||
769 | if (vtune->mode != VIDEO_MODE_AUTO) | ||
770 | return -EINVAL; | ||
771 | return 0; | ||
772 | } | ||
773 | case VIDIOCGPICT: | ||
774 | { | ||
775 | struct video_picture vpic = { | ||
776 | cam->brightness << 8, // brightness | ||
777 | (cam->hue + 128) << 8, // hue | ||
778 | cam->color << 9, // color | ||
779 | cam->contrast << 9, // contrast | ||
780 | 0x8000, // whiteness | ||
781 | 16, VIDEO_PALETTE_YUV422// bpp, palette format | ||
782 | }; | ||
783 | struct video_picture *pic = arg; | ||
784 | *pic = vpic; | ||
785 | return 0; | ||
786 | } | ||
787 | case VIDIOCSPICT: | ||
788 | { | ||
789 | struct video_picture *vpic = arg; | ||
790 | if (vpic->depth != 16 || vpic->palette != VIDEO_PALETTE_YUV422) | ||
791 | return -EINVAL; | ||
792 | |||
793 | cam->brightness = vpic->brightness >> 8; | ||
794 | cam->hue = (vpic->hue >> 8) - 128; | ||
795 | cam->color = vpic->colour >> 9; | ||
796 | cam->contrast = vpic->contrast >> 9; | ||
797 | |||
798 | w9966_pdev_claim(cam); | ||
799 | |||
800 | if ( | ||
801 | w9966_wReg_i2c(cam, 0x0a, cam->brightness) == -1 || | ||
802 | w9966_wReg_i2c(cam, 0x0b, cam->contrast) == -1 || | ||
803 | w9966_wReg_i2c(cam, 0x0c, cam->color) == -1 || | ||
804 | w9966_wReg_i2c(cam, 0x0d, cam->hue) == -1 | ||
805 | ) { | ||
806 | w9966_pdev_release(cam); | ||
807 | return -EIO; | ||
808 | } | ||
809 | |||
810 | w9966_pdev_release(cam); | ||
811 | return 0; | ||
812 | } | ||
813 | case VIDIOCSWIN: | ||
814 | { | ||
815 | int ret; | ||
816 | struct video_window *vwin = arg; | ||
817 | |||
818 | if (vwin->flags != 0) | ||
819 | return -EINVAL; | ||
820 | if (vwin->clipcount != 0) | ||
821 | return -EINVAL; | ||
822 | if (vwin->width < 2 || vwin->width > W9966_WND_MAX_W) | ||
823 | return -EINVAL; | ||
824 | if (vwin->height < 1 || vwin->height > W9966_WND_MAX_H) | ||
825 | return -EINVAL; | ||
826 | |||
827 | // Update camera regs | ||
828 | w9966_pdev_claim(cam); | ||
829 | ret = w9966_setup(cam, 0, 0, 1023, 1023, vwin->width, vwin->height); | ||
830 | w9966_pdev_release(cam); | ||
831 | |||
832 | if (ret != 0) { | ||
833 | DPRINTF("VIDIOCSWIN: w9966_setup() failed.\n"); | ||
834 | return -EIO; | ||
835 | } | ||
836 | |||
837 | return 0; | ||
838 | } | ||
839 | case VIDIOCGWIN: | ||
840 | { | ||
841 | struct video_window *vwin = arg; | ||
842 | memset(vwin, 0, sizeof(*vwin)); | ||
843 | vwin->width = cam->width; | ||
844 | vwin->height = cam->height; | ||
845 | return 0; | ||
846 | } | ||
847 | // Unimplemented | ||
848 | case VIDIOCCAPTURE: | ||
849 | case VIDIOCGFBUF: | ||
850 | case VIDIOCSFBUF: | ||
851 | case VIDIOCKEY: | ||
852 | case VIDIOCGFREQ: | ||
853 | case VIDIOCSFREQ: | ||
854 | case VIDIOCGAUDIO: | ||
855 | case VIDIOCSAUDIO: | ||
856 | return -EINVAL; | ||
857 | default: | ||
858 | return -ENOIOCTLCMD; | ||
859 | } | ||
860 | return 0; | ||
861 | } | ||
862 | |||
863 | static int w9966_v4l_ioctl(struct inode *inode, struct file *file, | ||
864 | unsigned int cmd, unsigned long arg) | ||
865 | { | ||
866 | return video_usercopy(inode, file, cmd, arg, w9966_v4l_do_ioctl); | ||
867 | } | ||
868 | |||
869 | // Capture data | ||
870 | static ssize_t w9966_v4l_read(struct file *file, char __user *buf, | ||
871 | size_t count, loff_t *ppos) | ||
872 | { | ||
873 | struct video_device *vdev = video_devdata(file); | ||
874 | struct w9966_dev *cam = vdev->priv; | ||
875 | unsigned char addr = 0xa0; // ECP, read, CCD-transfer, 00000 | ||
876 | unsigned char __user *dest = (unsigned char __user *)buf; | ||
877 | unsigned long dleft = count; | ||
878 | unsigned char *tbuf; | ||
879 | |||
880 | // Why would anyone want more than this?? | ||
881 | if (count > cam->width * cam->height * 2) | ||
882 | return -EINVAL; | ||
883 | |||
884 | w9966_pdev_claim(cam); | ||
885 | w9966_wReg(cam, 0x00, 0x02); // Reset ECP-FIFO buffer | ||
886 | w9966_wReg(cam, 0x00, 0x00); // Return to normal operation | ||
887 | w9966_wReg(cam, 0x01, 0x98); // Enable capture | ||
888 | |||
889 | // write special capture-addr and negotiate into data transfer | ||
890 | if ( | ||
891 | (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0 )|| | ||
892 | (parport_write(cam->pport, &addr, 1) != 1 )|| | ||
893 | (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0 ) | ||
894 | ) { | ||
895 | w9966_pdev_release(cam); | ||
896 | return -EFAULT; | ||
897 | } | ||
898 | |||
899 | tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL); | ||
900 | if (tbuf == NULL) { | ||
901 | count = -ENOMEM; | ||
902 | goto out; | ||
903 | } | ||
904 | |||
905 | while(dleft > 0) | ||
906 | { | ||
907 | unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft; | ||
908 | |||
909 | if (parport_read(cam->pport, tbuf, tsize) < tsize) { | ||
910 | count = -EFAULT; | ||
911 | goto out; | ||
912 | } | ||
913 | if (copy_to_user(dest, tbuf, tsize) != 0) { | ||
914 | count = -EFAULT; | ||
915 | goto out; | ||
916 | } | ||
917 | dest += tsize; | ||
918 | dleft -= tsize; | ||
919 | } | ||
920 | |||
921 | w9966_wReg(cam, 0x01, 0x18); // Disable capture | ||
922 | |||
923 | out: | ||
924 | kfree(tbuf); | ||
925 | w9966_pdev_release(cam); | ||
926 | |||
927 | return count; | ||
928 | } | ||
929 | |||
930 | |||
931 | // Called once for every parport on init | ||
932 | static void w9966_attach(struct parport *port) | ||
933 | { | ||
934 | int i; | ||
935 | |||
936 | for (i = 0; i < W9966_MAXCAMS; i++) | ||
937 | { | ||
938 | if (w9966_cams[i].dev_state != 0) // Cam is already assigned | ||
939 | continue; | ||
940 | if ( | ||
941 | strcmp(pardev[i], "aggressive") == 0 || | ||
942 | strcmp(pardev[i], port->name) == 0 | ||
943 | ) { | ||
944 | if (w9966_init(&w9966_cams[i], port) != 0) | ||
945 | w9966_term(&w9966_cams[i]); | ||
946 | break; // return | ||
947 | } | ||
948 | } | ||
949 | } | ||
950 | |||
951 | // Called once for every parport on termination | ||
952 | static void w9966_detach(struct parport *port) | ||
953 | { | ||
954 | int i; | ||
955 | for (i = 0; i < W9966_MAXCAMS; i++) | ||
956 | if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port) | ||
957 | w9966_term(&w9966_cams[i]); | ||
958 | } | ||
959 | |||
960 | |||
961 | static struct parport_driver w9966_ppd = { | ||
962 | .name = W9966_DRIVERNAME, | ||
963 | .attach = w9966_attach, | ||
964 | .detach = w9966_detach, | ||
965 | }; | ||
966 | |||
967 | // Module entry point | ||
968 | static int __init w9966_mod_init(void) | ||
969 | { | ||
970 | int i; | ||
971 | for (i = 0; i < W9966_MAXCAMS; i++) | ||
972 | w9966_cams[i].dev_state = 0; | ||
973 | |||
974 | return parport_register_driver(&w9966_ppd); | ||
975 | } | ||
976 | |||
977 | // Module cleanup | ||
978 | static void __exit w9966_mod_term(void) | ||
979 | { | ||
980 | parport_unregister_driver(&w9966_ppd); | ||
981 | } | ||
982 | |||
983 | module_init(w9966_mod_init); | ||
984 | module_exit(w9966_mod_term); | ||