blob: 7d5be07004138fac84ec7d41430cebbd7e94d22e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030029
Linus Torvalds1da177e2005-04-16 15:20:36 -070030 *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!
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030050
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 Changes:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030052
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 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>
Mauro Carvalho Chehab7e0a16f2009-03-10 05:31:34 -030060#include <linux/videodev.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090061#include <linux/slab.h>
Mauro Carvalho Chehab5e87efa2006-06-05 10:26:32 -030062#include <media/v4l2-common.h>
Hans Verkuil35ea11f2008-07-20 08:12:02 -030063#include <media/v4l2-ioctl.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070064#include <linux/parport.h>
65
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030066/*#define DEBUG*/ /* Undef me for production */
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68#ifdef DEBUG
Harvey Harrison7e28adb2008-04-08 23:20:00 -030069#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a)
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#else
71#define DPRINTF(x...)
72#endif
73
74/*
75 * Defines, simple typedefs etc.
76 */
77
78#define W9966_DRIVERNAME "W9966CF Webcam"
Hans Verkuil4bfdd582010-03-22 04:47:27 -030079#define W9966_MAXCAMS 4 /* Maximum number of cameras */
80#define W9966_RBUFFER 2048 /* Read buffer (must be an even number) */
81#define W9966_SRAMSIZE 131072 /* 128kb */
82#define W9966_SRAMID 0x02 /* check w9966cf.pdf */
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Hans Verkuil4bfdd582010-03-22 04:47:27 -030084/* Empirically determined window limits */
Linus Torvalds1da177e2005-04-16 15:20:36 -070085#define W9966_WND_MIN_X 16
86#define W9966_WND_MIN_Y 14
87#define W9966_WND_MAX_X 705
88#define W9966_WND_MAX_Y 253
89#define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X)
90#define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y)
91
Hans Verkuil4bfdd582010-03-22 04:47:27 -030092/* Keep track of our current state */
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#define W9966_STATE_PDEV 0x01
94#define W9966_STATE_CLAIMED 0x02
95#define W9966_STATE_VDEV 0x04
96
97#define W9966_I2C_W_ID 0x48
98#define W9966_I2C_R_ID 0x49
99#define W9966_I2C_R_DATA 0x08
100#define W9966_I2C_R_CLOCK 0x04
101#define W9966_I2C_W_DATA 0x02
102#define W9966_I2C_W_CLOCK 0x01
103
104struct w9966_dev {
105 unsigned char dev_state;
106 unsigned char i2c_state;
107 unsigned short ppmode;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300108 struct parport *pport;
109 struct pardevice *pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 struct video_device vdev;
111 unsigned short width;
112 unsigned short height;
113 unsigned char brightness;
114 signed char contrast;
115 signed char color;
116 signed char hue;
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300117 unsigned long in_use;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118};
119
120/*
121 * Module specific properties
122 */
123
124MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>");
125MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
126MODULE_LICENSE("GPL");
127
128
129#ifdef MODULE
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300130static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131#else
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300132static const char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133#endif
134module_param_array(pardev, charp, NULL, 0);
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300135MODULE_PARM_DESC(pardev, "pardev: where to search for\n"
136 "\teach camera. 'aggressive' means brute-force search.\n"
137 "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n"
138 "\tcam 1 to parport3 and search every parport for cam 2 etc...");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -0300140static int parmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141module_param(parmode, int, 0);
142MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp");
143
144static int video_nr = -1;
145module_param(video_nr, int, 0);
146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147static struct w9966_dev w9966_cams[W9966_MAXCAMS];
148
149/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 * Private function defines
151 */
152
153
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300154/* Set camera phase flags, so we know what to uninit when terminating */
155static inline void w9966_setState(struct w9966_dev *cam, int mask, int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156{
157 cam->dev_state = (cam->dev_state & ~mask) ^ val;
158}
159
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300160/* Get camera phase flags */
161static inline int w9966_getState(struct w9966_dev *cam, int mask, int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162{
163 return ((cam->dev_state & mask) == val);
164}
165
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300166/* Claim parport for ourself */
Hans Verkuil271922c2010-03-22 05:13:17 -0300167static void w9966_pdev_claim(struct w9966_dev *cam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168{
169 if (w9966_getState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED))
170 return;
171 parport_claim_or_block(cam->pdev);
172 w9966_setState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED);
173}
174
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300175/* Release parport for others to use */
Hans Verkuil271922c2010-03-22 05:13:17 -0300176static void w9966_pdev_release(struct w9966_dev *cam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177{
178 if (w9966_getState(cam, W9966_STATE_CLAIMED, 0))
179 return;
180 parport_release(cam->pdev);
181 w9966_setState(cam, W9966_STATE_CLAIMED, 0);
182}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300183
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300184/* Read register from W9966 interface-chip
185 Expects a claimed pdev
186 -1 on error, else register data (byte) */
187static int w9966_rReg(struct w9966_dev *cam, int reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188{
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300189 /* ECP, read, regtransfer, REG, REG, REG, REG, REG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 const unsigned char addr = 0x80 | (reg & 0x1f);
191 unsigned char val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300192
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
194 return -1;
195 if (parport_write(cam->pport, &addr, 1) != 1)
196 return -1;
197 if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
198 return -1;
199 if (parport_read(cam->pport, &val, 1) != 1)
200 return -1;
201
202 return val;
203}
204
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300205/* Write register to W9966 interface-chip
206 Expects a claimed pdev
207 -1 on error */
208static int w9966_wReg(struct w9966_dev *cam, int reg, int data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300210 /* ECP, write, regtransfer, REG, REG, REG, REG, REG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 const unsigned char addr = 0xc0 | (reg & 0x1f);
212 const unsigned char val = data;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300213
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
215 return -1;
216 if (parport_write(cam->pport, &addr, 1) != 1)
217 return -1;
218 if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
219 return -1;
220 if (parport_write(cam->pport, &val, 1) != 1)
221 return -1;
222
223 return 0;
224}
225
Hans Verkuil271922c2010-03-22 05:13:17 -0300226/*
227 * Ugly and primitive i2c protocol functions
228 */
229
230/* Sets the data line on the i2c bus.
231 Expects a claimed pdev. */
232static void w9966_i2c_setsda(struct w9966_dev *cam, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
Hans Verkuil271922c2010-03-22 05:13:17 -0300234 if (state)
235 cam->i2c_state |= W9966_I2C_W_DATA;
236 else
237 cam->i2c_state &= ~W9966_I2C_W_DATA;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300238
Hans Verkuil271922c2010-03-22 05:13:17 -0300239 w9966_wReg(cam, 0x18, cam->i2c_state);
240 udelay(5);
241}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
Hans Verkuil271922c2010-03-22 05:13:17 -0300243/* Get peripheral clock line
244 Expects a claimed pdev. */
245static int w9966_i2c_getscl(struct w9966_dev *cam)
246{
247 const unsigned char state = w9966_rReg(cam, 0x18);
248 return ((state & W9966_I2C_R_CLOCK) > 0);
249}
250
251/* Sets the clock line on the i2c bus.
252 Expects a claimed pdev. -1 on error */
253static int w9966_i2c_setscl(struct w9966_dev *cam, int state)
254{
255 unsigned long timeout;
256
257 if (state)
258 cam->i2c_state |= W9966_I2C_W_CLOCK;
259 else
260 cam->i2c_state &= ~W9966_I2C_W_CLOCK;
261
262 w9966_wReg(cam, 0x18, cam->i2c_state);
263 udelay(5);
264
265 /* we go to high, we also expect the peripheral to ack. */
266 if (state) {
267 timeout = jiffies + 100;
268 while (!w9966_i2c_getscl(cam)) {
269 if (time_after(jiffies, timeout))
270 return -1;
271 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 return 0;
274}
275
Hans Verkuil271922c2010-03-22 05:13:17 -0300276#if 0
277/* Get peripheral data line
278 Expects a claimed pdev. */
279static int w9966_i2c_getsda(struct w9966_dev *cam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280{
Hans Verkuil271922c2010-03-22 05:13:17 -0300281 const unsigned char state = w9966_rReg(cam, 0x18);
282 return ((state & W9966_I2C_R_DATA) > 0);
283}
284#endif
285
286/* Write a byte with ack to the i2c bus.
287 Expects a claimed pdev. -1 on error */
288static int w9966_i2c_wbyte(struct w9966_dev *cam, int data)
289{
290 int i;
291
292 for (i = 7; i >= 0; i--) {
293 w9966_i2c_setsda(cam, (data >> i) & 0x01);
294
295 if (w9966_i2c_setscl(cam, 1) == -1)
296 return -1;
297 w9966_i2c_setscl(cam, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 }
299
Hans Verkuil271922c2010-03-22 05:13:17 -0300300 w9966_i2c_setsda(cam, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Hans Verkuil271922c2010-03-22 05:13:17 -0300302 if (w9966_i2c_setscl(cam, 1) == -1)
303 return -1;
304 w9966_i2c_setscl(cam, 0);
305
306 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307}
308
Hans Verkuil271922c2010-03-22 05:13:17 -0300309/* Read a data byte with ack from the i2c-bus
310 Expects a claimed pdev. -1 on error */
311#if 0
312static int w9966_i2c_rbyte(struct w9966_dev *cam)
313{
314 unsigned char data = 0x00;
315 int i;
316
317 w9966_i2c_setsda(cam, 1);
318
319 for (i = 0; i < 8; i++) {
320 if (w9966_i2c_setscl(cam, 1) == -1)
321 return -1;
322 data = data << 1;
323 if (w9966_i2c_getsda(cam))
324 data |= 0x01;
325
326 w9966_i2c_setscl(cam, 0);
327 }
328 return data;
329}
330#endif
331
332/* Read a register from the i2c device.
333 Expects claimed pdev. -1 on error */
334#if 0
335static int w9966_rReg_i2c(struct w9966_dev *cam, int reg)
336{
337 int data;
338
339 w9966_i2c_setsda(cam, 0);
340 w9966_i2c_setscl(cam, 0);
341
342 if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
343 w9966_i2c_wbyte(cam, reg) == -1)
344 return -1;
345
346 w9966_i2c_setsda(cam, 1);
347 if (w9966_i2c_setscl(cam, 1) == -1)
348 return -1;
349 w9966_i2c_setsda(cam, 0);
350 w9966_i2c_setscl(cam, 0);
351
352 if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1)
353 return -1;
354 data = w9966_i2c_rbyte(cam);
355 if (data == -1)
356 return -1;
357
358 w9966_i2c_setsda(cam, 0);
359
360 if (w9966_i2c_setscl(cam, 1) == -1)
361 return -1;
362 w9966_i2c_setsda(cam, 1);
363
364 return data;
365}
366#endif
367
368/* Write a register to the i2c device.
369 Expects claimed pdev. -1 on error */
370static int w9966_wReg_i2c(struct w9966_dev *cam, int reg, int data)
371{
372 w9966_i2c_setsda(cam, 0);
373 w9966_i2c_setscl(cam, 0);
374
375 if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
376 w9966_i2c_wbyte(cam, reg) == -1 ||
377 w9966_i2c_wbyte(cam, data) == -1)
378 return -1;
379
380 w9966_i2c_setsda(cam, 0);
381 if (w9966_i2c_setscl(cam, 1) == -1)
382 return -1;
383
384 w9966_i2c_setsda(cam, 1);
385
386 return 0;
387}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300389/* Find a good length for capture window (used both for W and H)
390 A bit ugly but pretty functional. The capture length
391 have to match the downscale */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392static int w9966_findlen(int near, int size, int maxlen)
393{
394 int bestlen = size;
395 int besterr = abs(near - bestlen);
396 int len;
397
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300398 for (len = size + 1; len < maxlen; len++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 int err;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300400 if (((64 * size) % len) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 continue;
402
403 err = abs(near - len);
404
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300405 /* Only continue as long as we keep getting better values */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 if (err > besterr)
407 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300408
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 besterr = err;
410 bestlen = len;
411 }
412
413 return bestlen;
414}
415
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300416/* Modify capture window (if necessary)
417 and calculate downscaling
418 Return -1 on error */
419static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
421 int maxlen = max - min;
422 int len = *end - *beg + 1;
423 int newlen = w9966_findlen(len, size, maxlen);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300424 int err = newlen - len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300426 /* Check for bad format */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 if (newlen > maxlen || newlen < size)
428 return -1;
429
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300430 /* Set factor (6 bit fixed) */
431 *factor = (64 * size) / newlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 if (*factor == 64)
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300433 *factor = 0x00; /* downscale is disabled */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 else
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300435 *factor |= 0x80; /* set downscale-enable bit */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300437 /* Modify old beginning and end */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 *beg -= err / 2;
439 *end += err - (err / 2);
440
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300441 /* Move window if outside borders */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 if (*beg < min) {
443 *end += min - *beg;
444 *beg += min - *beg;
445 }
446 if (*end > max) {
447 *beg -= *end - max;
448 *end -= *end - max;
449 }
450
451 return 0;
452}
453
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300454/* Setup the cameras capture window etc.
455 Expects a claimed pdev
456 return -1 on error */
457static int w9966_setup(struct w9966_dev *cam, int x1, int y1, int x2, int y2, int w, int h)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458{
459 unsigned int i;
460 unsigned int enh_s, enh_e;
461 unsigned char scale_x, scale_y;
462 unsigned char regs[0x1c];
463 unsigned char saa7111_regs[] = {
464 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00,
465 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00,
466 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
467 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0
468 };
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300469
470
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300471 if (w * h * 2 > W9966_SRAMSIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 DPRINTF("capture window exceeds SRAM size!.\n");
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300473 w = 200; h = 160; /* Pick default values */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 }
475
476 w &= ~0x1;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300477 if (w < 2)
478 w = 2;
479 if (h < 1)
480 h = 1;
481 if (w > W9966_WND_MAX_W)
482 w = W9966_WND_MAX_W;
483 if (h > W9966_WND_MAX_H)
484 h = W9966_WND_MAX_H;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486 cam->width = w;
487 cam->height = h;
488
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300489 enh_s = 0;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300490 enh_e = w * h * 2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300491
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300492/* Modify capture window if necessary and calculate downscaling */
493 if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 ||
494 w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0)
495 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300497 DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n",
498 w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300499
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300500/* Setup registers */
501 regs[0x00] = 0x00; /* Set normal operation */
502 regs[0x01] = 0x18; /* Capture mode */
503 regs[0x02] = scale_y; /* V-scaling */
504 regs[0x03] = scale_x; /* H-scaling */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300505
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300506 /* Capture window */
507 regs[0x04] = (x1 & 0x0ff); /* X-start (8 low bits) */
508 regs[0x05] = (x1 & 0x300)>>8; /* X-start (2 high bits) */
509 regs[0x06] = (y1 & 0x0ff); /* Y-start (8 low bits) */
510 regs[0x07] = (y1 & 0x300)>>8; /* Y-start (2 high bits) */
511 regs[0x08] = (x2 & 0x0ff); /* X-end (8 low bits) */
512 regs[0x09] = (x2 & 0x300)>>8; /* X-end (2 high bits) */
513 regs[0x0a] = (y2 & 0x0ff); /* Y-end (8 low bits) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300515 regs[0x0c] = W9966_SRAMID; /* SRAM-banks (1x 128kb) */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300516
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300517 /* Enhancement layer */
518 regs[0x0d] = (enh_s & 0x000ff); /* Enh. start (0-7) */
519 regs[0x0e] = (enh_s & 0x0ff00) >> 8; /* Enh. start (8-15) */
520 regs[0x0f] = (enh_s & 0x70000) >> 16; /* Enh. start (16-17/18??) */
521 regs[0x10] = (enh_e & 0x000ff); /* Enh. end (0-7) */
522 regs[0x11] = (enh_e & 0x0ff00) >> 8; /* Enh. end (8-15) */
523 regs[0x12] = (enh_e & 0x70000) >> 16; /* Enh. end (16-17/18??) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300525 /* Misc */
526 regs[0x13] = 0x40; /* VEE control (raw 4:2:2) */
527 regs[0x17] = 0x00; /* ??? */
528 regs[0x18] = cam->i2c_state = 0x00; /* Serial bus */
529 regs[0x19] = 0xff; /* I/O port direction control */
530 regs[0x1a] = 0xff; /* I/O port data register */
531 regs[0x1b] = 0x10; /* ??? */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300532
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300533 /* SAA7111 chip settings */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 saa7111_regs[0x0a] = cam->brightness;
535 saa7111_regs[0x0b] = cam->contrast;
536 saa7111_regs[0x0c] = cam->color;
537 saa7111_regs[0x0d] = cam->hue;
538
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300539/* Reset (ECP-fifo & serial-bus) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 if (w9966_wReg(cam, 0x00, 0x03) == -1)
541 return -1;
542
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300543/* Write regs to w9966cf chip */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 for (i = 0; i < 0x1c; i++)
545 if (w9966_wReg(cam, i, regs[i]) == -1)
546 return -1;
547
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300548/* Write regs to saa7111 chip */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 for (i = 0; i < 0x20; i++)
550 if (w9966_wReg_i2c(cam, i, saa7111_regs[i]) == -1)
551 return -1;
552
553 return 0;
554}
555
556/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 * Video4linux interfacing
558 */
559
Hans Verkuil069b7472008-12-30 07:04:34 -0300560static long w9966_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561{
Hans Verkuilc170ecf2008-08-23 08:32:09 -0300562 struct w9966_dev *cam = video_drvdata(file);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300563
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300564 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 case VIDIOCGCAP:
566 {
567 static struct video_capability vcap = {
568 .name = W9966_DRIVERNAME,
569 .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES,
570 .channels = 1,
571 .maxwidth = W9966_WND_MAX_W,
572 .maxheight = W9966_WND_MAX_H,
573 .minwidth = 2,
574 .minheight = 1,
575 };
576 struct video_capability *cap = arg;
577 *cap = vcap;
578 return 0;
579 }
580 case VIDIOCGCHAN:
581 {
582 struct video_channel *vch = arg;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300583 if (vch->channel != 0) /* We only support one channel (#0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 return -EINVAL;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300585 memset(vch, 0, sizeof(*vch));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 strcpy(vch->name, "CCD-input");
587 vch->type = VIDEO_TYPE_CAMERA;
588 return 0;
589 }
590 case VIDIOCSCHAN:
591 {
592 struct video_channel *vch = arg;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300593 if (vch->channel != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 return -EINVAL;
595 return 0;
596 }
597 case VIDIOCGTUNER:
598 {
599 struct video_tuner *vtune = arg;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300600 if (vtune->tuner != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 return -EINVAL;
602 strcpy(vtune->name, "no tuner");
603 vtune->rangelow = 0;
604 vtune->rangehigh = 0;
605 vtune->flags = VIDEO_TUNER_NORM;
606 vtune->mode = VIDEO_MODE_AUTO;
607 vtune->signal = 0xffff;
608 return 0;
609 }
610 case VIDIOCSTUNER:
611 {
612 struct video_tuner *vtune = arg;
613 if (vtune->tuner != 0)
614 return -EINVAL;
615 if (vtune->mode != VIDEO_MODE_AUTO)
616 return -EINVAL;
617 return 0;
618 }
619 case VIDIOCGPICT:
620 {
621 struct video_picture vpic = {
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300622 cam->brightness << 8, /* brightness */
623 (cam->hue + 128) << 8, /* hue */
624 cam->color << 9, /* color */
625 cam->contrast << 9, /* contrast */
626 0x8000, /* whiteness */
627 16, VIDEO_PALETTE_YUV422/* bpp, palette format */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 };
629 struct video_picture *pic = arg;
630 *pic = vpic;
631 return 0;
632 }
633 case VIDIOCSPICT:
634 {
635 struct video_picture *vpic = arg;
audetto@tiscali.it2485eb02006-12-12 10:35:57 -0300636 if (vpic->depth != 16 || (vpic->palette != VIDEO_PALETTE_YUV422 && vpic->palette != VIDEO_PALETTE_YUYV))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 return -EINVAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300638
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 cam->brightness = vpic->brightness >> 8;
640 cam->hue = (vpic->hue >> 8) - 128;
641 cam->color = vpic->colour >> 9;
642 cam->contrast = vpic->contrast >> 9;
643
644 w9966_pdev_claim(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300645
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 if (
647 w9966_wReg_i2c(cam, 0x0a, cam->brightness) == -1 ||
648 w9966_wReg_i2c(cam, 0x0b, cam->contrast) == -1 ||
649 w9966_wReg_i2c(cam, 0x0c, cam->color) == -1 ||
650 w9966_wReg_i2c(cam, 0x0d, cam->hue) == -1
651 ) {
652 w9966_pdev_release(cam);
653 return -EIO;
654 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300655
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 w9966_pdev_release(cam);
657 return 0;
658 }
659 case VIDIOCSWIN:
660 {
661 int ret;
662 struct video_window *vwin = arg;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300663
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 if (vwin->flags != 0)
665 return -EINVAL;
666 if (vwin->clipcount != 0)
667 return -EINVAL;
668 if (vwin->width < 2 || vwin->width > W9966_WND_MAX_W)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300669 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 if (vwin->height < 1 || vwin->height > W9966_WND_MAX_H)
671 return -EINVAL;
672
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300673 /* Update camera regs */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 w9966_pdev_claim(cam);
675 ret = w9966_setup(cam, 0, 0, 1023, 1023, vwin->width, vwin->height);
676 w9966_pdev_release(cam);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300677
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 if (ret != 0) {
679 DPRINTF("VIDIOCSWIN: w9966_setup() failed.\n");
680 return -EIO;
681 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return 0;
684 }
685 case VIDIOCGWIN:
686 {
687 struct video_window *vwin = arg;
688 memset(vwin, 0, sizeof(*vwin));
689 vwin->width = cam->width;
690 vwin->height = cam->height;
691 return 0;
692 }
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300693 /* Unimplemented */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300694 case VIDIOCCAPTURE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 case VIDIOCGFBUF:
696 case VIDIOCSFBUF:
697 case VIDIOCKEY:
698 case VIDIOCGFREQ:
699 case VIDIOCSFREQ:
700 case VIDIOCGAUDIO:
701 case VIDIOCSAUDIO:
702 return -EINVAL;
703 default:
704 return -ENOIOCTLCMD;
705 }
706 return 0;
707}
708
Hans Verkuil069b7472008-12-30 07:04:34 -0300709static long w9966_v4l_ioctl(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 unsigned int cmd, unsigned long arg)
711{
Hans Verkuilf473bf72008-11-01 08:25:11 -0300712 return video_usercopy(file, cmd, arg, w9966_v4l_do_ioctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713}
714
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300715/* Capture data */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716static ssize_t w9966_v4l_read(struct file *file, char __user *buf,
717 size_t count, loff_t *ppos)
718{
Hans Verkuilc170ecf2008-08-23 08:32:09 -0300719 struct w9966_dev *cam = video_drvdata(file);
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300720 unsigned char addr = 0xa0; /* ECP, read, CCD-transfer, 00000 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 unsigned char __user *dest = (unsigned char __user *)buf;
722 unsigned long dleft = count;
723 unsigned char *tbuf;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300724
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300725 /* Why would anyone want more than this?? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 if (count > cam->width * cam->height * 2)
727 return -EINVAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300728
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 w9966_pdev_claim(cam);
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300730 w9966_wReg(cam, 0x00, 0x02); /* Reset ECP-FIFO buffer */
731 w9966_wReg(cam, 0x00, 0x00); /* Return to normal operation */
732 w9966_wReg(cam, 0x01, 0x98); /* Enable capture */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300734 /* write special capture-addr and negotiate into data transfer */
735 if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) ||
736 (parport_write(cam->pport, &addr, 1) != 1) ||
737 (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 w9966_pdev_release(cam);
739 return -EFAULT;
740 }
741
742 tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL);
743 if (tbuf == NULL) {
744 count = -ENOMEM;
745 goto out;
746 }
747
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300748 while (dleft > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300750
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 if (parport_read(cam->pport, tbuf, tsize) < tsize) {
752 count = -EFAULT;
753 goto out;
754 }
755 if (copy_to_user(dest, tbuf, tsize) != 0) {
756 count = -EFAULT;
757 goto out;
758 }
759 dest += tsize;
760 dleft -= tsize;
761 }
762
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300763 w9966_wReg(cam, 0x01, 0x18); /* Disable capture */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
765out:
766 kfree(tbuf);
767 w9966_pdev_release(cam);
768
769 return count;
770}
771
Hans Verkuil271922c2010-03-22 05:13:17 -0300772static int w9966_exclusive_open(struct file *file)
773{
774 struct w9966_dev *cam = video_drvdata(file);
775
776 return test_and_set_bit(0, &cam->in_use) ? -EBUSY : 0;
777}
778
779static int w9966_exclusive_release(struct file *file)
780{
781 struct w9966_dev *cam = video_drvdata(file);
782
783 clear_bit(0, &cam->in_use);
784 return 0;
785}
786
787static const struct v4l2_file_operations w9966_fops = {
788 .owner = THIS_MODULE,
789 .open = w9966_exclusive_open,
790 .release = w9966_exclusive_release,
791 .ioctl = w9966_v4l_ioctl,
792 .read = w9966_v4l_read,
793};
794
795static struct video_device w9966_template = {
796 .name = W9966_DRIVERNAME,
797 .fops = &w9966_fops,
798 .release = video_device_release_empty,
799};
800
801
802/* Initialize camera device. Setup all internal flags, set a
803 default video mode, setup ccd-chip, register v4l device etc..
804 Also used for 'probing' of hardware.
805 -1 on error */
806static int w9966_init(struct w9966_dev *cam, struct parport* port)
807{
808 if (cam->dev_state != 0)
809 return -1;
810
811 cam->pport = port;
812 cam->brightness = 128;
813 cam->contrast = 64;
814 cam->color = 64;
815 cam->hue = 0;
816
817/* Select requested transfer mode */
818 switch (parmode) {
819 default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */
820 case 0:
821 if (port->modes & PARPORT_MODE_ECP)
822 cam->ppmode = IEEE1284_MODE_ECP;
823 else if (port->modes & PARPORT_MODE_EPP)
824 cam->ppmode = IEEE1284_MODE_EPP;
825 else
826 cam->ppmode = IEEE1284_MODE_ECP;
827 break;
828 case 1: /* hw- or sw-ecp */
829 cam->ppmode = IEEE1284_MODE_ECP;
830 break;
831 case 2: /* hw- or sw-epp */
832 cam->ppmode = IEEE1284_MODE_EPP;
833 break;
834 }
835
836/* Tell the parport driver that we exists */
837 cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL);
838 if (cam->pdev == NULL) {
839 DPRINTF("parport_register_device() failed\n");
840 return -1;
841 }
842 w9966_setState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV);
843
844 w9966_pdev_claim(cam);
845
846/* Setup a default capture mode */
847 if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) {
848 DPRINTF("w9966_setup() failed.\n");
849 return -1;
850 }
851
852 w9966_pdev_release(cam);
853
854/* Fill in the video_device struct and register us to v4l */
855 memcpy(&cam->vdev, &w9966_template, sizeof(struct video_device));
856 video_set_drvdata(&cam->vdev, cam);
857
858 if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
859 return -1;
860
861 w9966_setState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV);
862
863 /* All ok */
864 printk(KERN_INFO "w9966cf: Found and initialized a webcam on %s.\n",
865 cam->pport->name);
866 return 0;
867}
868
869
870/* Terminate everything gracefully */
871static void w9966_term(struct w9966_dev *cam)
872{
873/* Unregister from v4l */
874 if (w9966_getState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) {
875 video_unregister_device(&cam->vdev);
876 w9966_setState(cam, W9966_STATE_VDEV, 0);
877 }
878
879/* Terminate from IEEE1284 mode and release pdev block */
880 if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
881 w9966_pdev_claim(cam);
882 parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT);
883 w9966_pdev_release(cam);
884 }
885
886/* Unregister from parport */
887 if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
888 parport_unregister_device(cam->pdev);
889 w9966_setState(cam, W9966_STATE_PDEV, 0);
890 }
891}
892
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300894/* Called once for every parport on init */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895static void w9966_attach(struct parport *port)
896{
897 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300898
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300899 for (i = 0; i < W9966_MAXCAMS; i++) {
900 if (w9966_cams[i].dev_state != 0) /* Cam is already assigned */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 continue;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300902 if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 if (w9966_init(&w9966_cams[i], port) != 0)
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300904 w9966_term(&w9966_cams[i]);
905 break; /* return */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 }
907 }
908}
909
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300910/* Called once for every parport on termination */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911static void w9966_detach(struct parport *port)
912{
913 int i;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300914
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 for (i = 0; i < W9966_MAXCAMS; i++)
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300916 if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port)
917 w9966_term(&w9966_cams[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918}
919
920
921static struct parport_driver w9966_ppd = {
922 .name = W9966_DRIVERNAME,
923 .attach = w9966_attach,
924 .detach = w9966_detach,
925};
926
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300927/* Module entry point */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928static int __init w9966_mod_init(void)
929{
930 int i;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300931
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 for (i = 0; i < W9966_MAXCAMS; i++)
933 w9966_cams[i].dev_state = 0;
934
935 return parport_register_driver(&w9966_ppd);
936}
937
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300938/* Module cleanup */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939static void __exit w9966_mod_term(void)
940{
941 parport_unregister_driver(&w9966_ppd);
942}
943
944module_init(w9966_mod_init);
945module_exit(w9966_mod_term);