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