blob: 453dbbd1e6e8636f9d38e8b6d7543c880ed2ae41 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 Winbond w9966cf Webcam parport driver.
3
Hans Verkuil626e2ac2010-04-06 11:36:39 -03004 Version 0.33
Linus Torvalds1da177e2005-04-16 15:20:36 -07005
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>
Hans Verkuil626e2ac2010-04-06 11:36:39 -030060#include <linux/videodev2.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>
Hans Verkuil626e2ac2010-04-06 11:36:39 -030064#include <media/v4l2-device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#include <linux/parport.h>
66
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030067/*#define DEBUG*/ /* Undef me for production */
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69#ifdef DEBUG
Harvey Harrison7e28adb2008-04-08 23:20:00 -030070#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071#else
72#define DPRINTF(x...)
73#endif
74
75/*
76 * Defines, simple typedefs etc.
77 */
78
79#define W9966_DRIVERNAME "W9966CF Webcam"
Hans Verkuil4bfdd582010-03-22 04:47:27 -030080#define W9966_MAXCAMS 4 /* Maximum number of cameras */
81#define W9966_RBUFFER 2048 /* Read buffer (must be an even number) */
82#define W9966_SRAMSIZE 131072 /* 128kb */
83#define W9966_SRAMID 0x02 /* check w9966cf.pdf */
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
Hans Verkuil4bfdd582010-03-22 04:47:27 -030085/* Empirically determined window limits */
Linus Torvalds1da177e2005-04-16 15:20:36 -070086#define W9966_WND_MIN_X 16
87#define W9966_WND_MIN_Y 14
88#define W9966_WND_MAX_X 705
89#define W9966_WND_MAX_Y 253
90#define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X)
91#define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y)
92
Hans Verkuil4bfdd582010-03-22 04:47:27 -030093/* Keep track of our current state */
Linus Torvalds1da177e2005-04-16 15:20:36 -070094#define W9966_STATE_PDEV 0x01
95#define W9966_STATE_CLAIMED 0x02
96#define W9966_STATE_VDEV 0x04
97
98#define W9966_I2C_W_ID 0x48
99#define W9966_I2C_R_ID 0x49
100#define W9966_I2C_R_DATA 0x08
101#define W9966_I2C_R_CLOCK 0x04
102#define W9966_I2C_W_DATA 0x02
103#define W9966_I2C_W_CLOCK 0x01
104
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300105struct w9966 {
106 struct v4l2_device v4l2_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 unsigned char dev_state;
108 unsigned char i2c_state;
109 unsigned short ppmode;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300110 struct parport *pport;
111 struct pardevice *pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 struct video_device vdev;
113 unsigned short width;
114 unsigned short height;
115 unsigned char brightness;
116 signed char contrast;
117 signed char color;
118 signed char hue;
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300119 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120};
121
122/*
123 * Module specific properties
124 */
125
126MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>");
127MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
128MODULE_LICENSE("GPL");
Mauro Carvalho Chehab1990d502011-06-24 14:45:49 -0300129MODULE_VERSION("0.33.1");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
131#ifdef MODULE
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300132static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133#else
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300134static const char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135#endif
136module_param_array(pardev, charp, NULL, 0);
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300137MODULE_PARM_DESC(pardev, "pardev: where to search for\n"
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300138 "\teach camera. 'aggressive' means brute-force search.\n"
139 "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n"
140 "\tcam 1 to parport3 and search every parport for cam 2 etc...");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -0300142static int parmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143module_param(parmode, int, 0);
144MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp");
145
146static int video_nr = -1;
147module_param(video_nr, int, 0);
148
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300149static struct w9966 w9966_cams[W9966_MAXCAMS];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
151/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 * Private function defines
153 */
154
155
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300156/* Set camera phase flags, so we know what to uninit when terminating */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300157static inline void w9966_set_state(struct w9966 *cam, int mask, int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158{
159 cam->dev_state = (cam->dev_state & ~mask) ^ val;
160}
161
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300162/* Get camera phase flags */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300163static inline int w9966_get_state(struct w9966 *cam, int mask, int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164{
165 return ((cam->dev_state & mask) == val);
166}
167
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300168/* Claim parport for ourself */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300169static void w9966_pdev_claim(struct w9966 *cam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170{
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300171 if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 return;
173 parport_claim_or_block(cam->pdev);
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300174 w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175}
176
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300177/* Release parport for others to use */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300178static void w9966_pdev_release(struct w9966 *cam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179{
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300180 if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 return;
182 parport_release(cam->pdev);
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300183 w9966_set_state(cam, W9966_STATE_CLAIMED, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300185
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300186/* Read register from W9966 interface-chip
187 Expects a claimed pdev
188 -1 on error, else register data (byte) */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300189static int w9966_read_reg(struct w9966 *cam, int reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190{
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300191 /* ECP, read, regtransfer, REG, REG, REG, REG, REG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 const unsigned char addr = 0x80 | (reg & 0x1f);
193 unsigned char val;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
196 return -1;
197 if (parport_write(cam->pport, &addr, 1) != 1)
198 return -1;
199 if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
200 return -1;
201 if (parport_read(cam->pport, &val, 1) != 1)
202 return -1;
203
204 return val;
205}
206
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300207/* Write register to W9966 interface-chip
208 Expects a claimed pdev
209 -1 on error */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300210static int w9966_write_reg(struct w9966 *cam, int reg, int data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300212 /* ECP, write, regtransfer, REG, REG, REG, REG, REG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 const unsigned char addr = 0xc0 | (reg & 0x1f);
214 const unsigned char val = data;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300215
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
217 return -1;
218 if (parport_write(cam->pport, &addr, 1) != 1)
219 return -1;
220 if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
221 return -1;
222 if (parport_write(cam->pport, &val, 1) != 1)
223 return -1;
224
225 return 0;
226}
227
Hans Verkuil271922c2010-03-22 05:13:17 -0300228/*
229 * Ugly and primitive i2c protocol functions
230 */
231
232/* Sets the data line on the i2c bus.
233 Expects a claimed pdev. */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300234static void w9966_i2c_setsda(struct w9966 *cam, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235{
Hans Verkuil271922c2010-03-22 05:13:17 -0300236 if (state)
237 cam->i2c_state |= W9966_I2C_W_DATA;
238 else
239 cam->i2c_state &= ~W9966_I2C_W_DATA;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300240
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300241 w9966_write_reg(cam, 0x18, cam->i2c_state);
Hans Verkuil271922c2010-03-22 05:13:17 -0300242 udelay(5);
243}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Hans Verkuil271922c2010-03-22 05:13:17 -0300245/* Get peripheral clock line
246 Expects a claimed pdev. */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300247static int w9966_i2c_getscl(struct w9966 *cam)
Hans Verkuil271922c2010-03-22 05:13:17 -0300248{
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300249 const unsigned char state = w9966_read_reg(cam, 0x18);
Hans Verkuil271922c2010-03-22 05:13:17 -0300250 return ((state & W9966_I2C_R_CLOCK) > 0);
251}
252
253/* Sets the clock line on the i2c bus.
254 Expects a claimed pdev. -1 on error */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300255static int w9966_i2c_setscl(struct w9966 *cam, int state)
Hans Verkuil271922c2010-03-22 05:13:17 -0300256{
257 unsigned long timeout;
258
259 if (state)
260 cam->i2c_state |= W9966_I2C_W_CLOCK;
261 else
262 cam->i2c_state &= ~W9966_I2C_W_CLOCK;
263
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300264 w9966_write_reg(cam, 0x18, cam->i2c_state);
Hans Verkuil271922c2010-03-22 05:13:17 -0300265 udelay(5);
266
267 /* we go to high, we also expect the peripheral to ack. */
268 if (state) {
269 timeout = jiffies + 100;
270 while (!w9966_i2c_getscl(cam)) {
271 if (time_after(jiffies, timeout))
272 return -1;
273 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 return 0;
276}
277
Hans Verkuil271922c2010-03-22 05:13:17 -0300278#if 0
279/* Get peripheral data line
280 Expects a claimed pdev. */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300281static int w9966_i2c_getsda(struct w9966 *cam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300283 const unsigned char state = w9966_read_reg(cam, 0x18);
Hans Verkuil271922c2010-03-22 05:13:17 -0300284 return ((state & W9966_I2C_R_DATA) > 0);
285}
286#endif
287
288/* Write a byte with ack to the i2c bus.
289 Expects a claimed pdev. -1 on error */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300290static int w9966_i2c_wbyte(struct w9966 *cam, int data)
Hans Verkuil271922c2010-03-22 05:13:17 -0300291{
292 int i;
293
294 for (i = 7; i >= 0; i--) {
295 w9966_i2c_setsda(cam, (data >> i) & 0x01);
296
297 if (w9966_i2c_setscl(cam, 1) == -1)
298 return -1;
299 w9966_i2c_setscl(cam, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 }
301
Hans Verkuil271922c2010-03-22 05:13:17 -0300302 w9966_i2c_setsda(cam, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Hans Verkuil271922c2010-03-22 05:13:17 -0300304 if (w9966_i2c_setscl(cam, 1) == -1)
305 return -1;
306 w9966_i2c_setscl(cam, 0);
307
308 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309}
310
Hans Verkuil271922c2010-03-22 05:13:17 -0300311/* Read a data byte with ack from the i2c-bus
312 Expects a claimed pdev. -1 on error */
313#if 0
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300314static int w9966_i2c_rbyte(struct w9966 *cam)
Hans Verkuil271922c2010-03-22 05:13:17 -0300315{
316 unsigned char data = 0x00;
317 int i;
318
319 w9966_i2c_setsda(cam, 1);
320
321 for (i = 0; i < 8; i++) {
322 if (w9966_i2c_setscl(cam, 1) == -1)
323 return -1;
324 data = data << 1;
325 if (w9966_i2c_getsda(cam))
326 data |= 0x01;
327
328 w9966_i2c_setscl(cam, 0);
329 }
330 return data;
331}
332#endif
333
334/* Read a register from the i2c device.
335 Expects claimed pdev. -1 on error */
336#if 0
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300337static int w9966_read_reg_i2c(struct w9966 *cam, int reg)
Hans Verkuil271922c2010-03-22 05:13:17 -0300338{
339 int data;
340
341 w9966_i2c_setsda(cam, 0);
342 w9966_i2c_setscl(cam, 0);
343
344 if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
345 w9966_i2c_wbyte(cam, reg) == -1)
346 return -1;
347
348 w9966_i2c_setsda(cam, 1);
349 if (w9966_i2c_setscl(cam, 1) == -1)
350 return -1;
351 w9966_i2c_setsda(cam, 0);
352 w9966_i2c_setscl(cam, 0);
353
354 if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1)
355 return -1;
356 data = w9966_i2c_rbyte(cam);
357 if (data == -1)
358 return -1;
359
360 w9966_i2c_setsda(cam, 0);
361
362 if (w9966_i2c_setscl(cam, 1) == -1)
363 return -1;
364 w9966_i2c_setsda(cam, 1);
365
366 return data;
367}
368#endif
369
370/* Write a register to the i2c device.
371 Expects claimed pdev. -1 on error */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300372static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data)
Hans Verkuil271922c2010-03-22 05:13:17 -0300373{
374 w9966_i2c_setsda(cam, 0);
375 w9966_i2c_setscl(cam, 0);
376
377 if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300378 w9966_i2c_wbyte(cam, reg) == -1 ||
379 w9966_i2c_wbyte(cam, data) == -1)
Hans Verkuil271922c2010-03-22 05:13:17 -0300380 return -1;
381
382 w9966_i2c_setsda(cam, 0);
383 if (w9966_i2c_setscl(cam, 1) == -1)
384 return -1;
385
386 w9966_i2c_setsda(cam, 1);
387
388 return 0;
389}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300391/* Find a good length for capture window (used both for W and H)
392 A bit ugly but pretty functional. The capture length
393 have to match the downscale */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394static int w9966_findlen(int near, int size, int maxlen)
395{
396 int bestlen = size;
397 int besterr = abs(near - bestlen);
398 int len;
399
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300400 for (len = size + 1; len < maxlen; len++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 int err;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300402 if (((64 * size) % len) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 continue;
404
405 err = abs(near - len);
406
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300407 /* Only continue as long as we keep getting better values */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 if (err > besterr)
409 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300410
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 besterr = err;
412 bestlen = len;
413 }
414
415 return bestlen;
416}
417
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300418/* Modify capture window (if necessary)
419 and calculate downscaling
420 Return -1 on error */
421static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422{
423 int maxlen = max - min;
424 int len = *end - *beg + 1;
425 int newlen = w9966_findlen(len, size, maxlen);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300426 int err = newlen - len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300428 /* Check for bad format */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 if (newlen > maxlen || newlen < size)
430 return -1;
431
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300432 /* Set factor (6 bit fixed) */
433 *factor = (64 * size) / newlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 if (*factor == 64)
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300435 *factor = 0x00; /* downscale is disabled */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 else
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300437 *factor |= 0x80; /* set downscale-enable bit */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300439 /* Modify old beginning and end */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 *beg -= err / 2;
441 *end += err - (err / 2);
442
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300443 /* Move window if outside borders */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 if (*beg < min) {
445 *end += min - *beg;
446 *beg += min - *beg;
447 }
448 if (*end > max) {
449 *beg -= *end - max;
450 *end -= *end - max;
451 }
452
453 return 0;
454}
455
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300456/* Setup the cameras capture window etc.
457 Expects a claimed pdev
458 return -1 on error */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300459static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460{
461 unsigned int i;
462 unsigned int enh_s, enh_e;
463 unsigned char scale_x, scale_y;
464 unsigned char regs[0x1c];
465 unsigned char saa7111_regs[] = {
466 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00,
467 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00,
468 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
469 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0
470 };
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300471
472
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300473 if (w * h * 2 > W9966_SRAMSIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 DPRINTF("capture window exceeds SRAM size!.\n");
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300475 w = 200; h = 160; /* Pick default values */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 }
477
478 w &= ~0x1;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300479 if (w < 2)
480 w = 2;
481 if (h < 1)
482 h = 1;
483 if (w > W9966_WND_MAX_W)
484 w = W9966_WND_MAX_W;
485 if (h > W9966_WND_MAX_H)
486 h = W9966_WND_MAX_H;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
488 cam->width = w;
489 cam->height = h;
490
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300491 enh_s = 0;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300492 enh_e = w * h * 2;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300493
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300494 /* Modify capture window if necessary and calculate downscaling */
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300495 if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 ||
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300496 w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0)
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300497 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300499 DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n",
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300500 w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300501
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300502 /* Setup registers */
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300503 regs[0x00] = 0x00; /* Set normal operation */
504 regs[0x01] = 0x18; /* Capture mode */
505 regs[0x02] = scale_y; /* V-scaling */
506 regs[0x03] = scale_x; /* H-scaling */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300507
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300508 /* Capture window */
509 regs[0x04] = (x1 & 0x0ff); /* X-start (8 low bits) */
510 regs[0x05] = (x1 & 0x300)>>8; /* X-start (2 high bits) */
511 regs[0x06] = (y1 & 0x0ff); /* Y-start (8 low bits) */
512 regs[0x07] = (y1 & 0x300)>>8; /* Y-start (2 high bits) */
513 regs[0x08] = (x2 & 0x0ff); /* X-end (8 low bits) */
514 regs[0x09] = (x2 & 0x300)>>8; /* X-end (2 high bits) */
515 regs[0x0a] = (y2 & 0x0ff); /* Y-end (8 low bits) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300517 regs[0x0c] = W9966_SRAMID; /* SRAM-banks (1x 128kb) */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300518
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300519 /* Enhancement layer */
520 regs[0x0d] = (enh_s & 0x000ff); /* Enh. start (0-7) */
521 regs[0x0e] = (enh_s & 0x0ff00) >> 8; /* Enh. start (8-15) */
522 regs[0x0f] = (enh_s & 0x70000) >> 16; /* Enh. start (16-17/18??) */
523 regs[0x10] = (enh_e & 0x000ff); /* Enh. end (0-7) */
524 regs[0x11] = (enh_e & 0x0ff00) >> 8; /* Enh. end (8-15) */
525 regs[0x12] = (enh_e & 0x70000) >> 16; /* Enh. end (16-17/18??) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300527 /* Misc */
528 regs[0x13] = 0x40; /* VEE control (raw 4:2:2) */
529 regs[0x17] = 0x00; /* ??? */
530 regs[0x18] = cam->i2c_state = 0x00; /* Serial bus */
531 regs[0x19] = 0xff; /* I/O port direction control */
532 regs[0x1a] = 0xff; /* I/O port data register */
533 regs[0x1b] = 0x10; /* ??? */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300534
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300535 /* SAA7111 chip settings */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 saa7111_regs[0x0a] = cam->brightness;
537 saa7111_regs[0x0b] = cam->contrast;
538 saa7111_regs[0x0c] = cam->color;
539 saa7111_regs[0x0d] = cam->hue;
540
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300541 /* Reset (ECP-fifo & serial-bus) */
542 if (w9966_write_reg(cam, 0x00, 0x03) == -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 return -1;
544
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300545 /* Write regs to w9966cf chip */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 for (i = 0; i < 0x1c; i++)
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300547 if (w9966_write_reg(cam, i, regs[i]) == -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 return -1;
549
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300550 /* Write regs to saa7111 chip */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 for (i = 0; i < 0x20; i++)
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300552 if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 return -1;
554
555 return 0;
556}
557
558/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 * Video4linux interfacing
560 */
561
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300562static int cam_querycap(struct file *file, void *priv,
563 struct v4l2_capability *vcap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300565 struct w9966 *cam = video_drvdata(file);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300566
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300567 strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver));
568 strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card));
569 strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300570 vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 return 0;
572}
573
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300574static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575{
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300576 if (vin->index > 0)
577 return -EINVAL;
578 strlcpy(vin->name, "Camera", sizeof(vin->name));
579 vin->type = V4L2_INPUT_TYPE_CAMERA;
580 vin->audioset = 0;
581 vin->tuner = 0;
582 vin->std = 0;
583 vin->status = 0;
584 return 0;
585}
586
587static int cam_g_input(struct file *file, void *fh, unsigned int *inp)
588{
589 *inp = 0;
590 return 0;
591}
592
593static int cam_s_input(struct file *file, void *fh, unsigned int inp)
594{
595 return (inp > 0) ? -EINVAL : 0;
596}
597
598static int cam_queryctrl(struct file *file, void *priv,
599 struct v4l2_queryctrl *qc)
600{
601 switch (qc->id) {
602 case V4L2_CID_BRIGHTNESS:
603 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
604 case V4L2_CID_CONTRAST:
605 return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64);
606 case V4L2_CID_SATURATION:
607 return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64);
608 case V4L2_CID_HUE:
609 return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
610 }
611 return -EINVAL;
612}
613
614static int cam_g_ctrl(struct file *file, void *priv,
615 struct v4l2_control *ctrl)
616{
617 struct w9966 *cam = video_drvdata(file);
618 int ret = 0;
619
620 switch (ctrl->id) {
621 case V4L2_CID_BRIGHTNESS:
622 ctrl->value = cam->brightness;
623 break;
624 case V4L2_CID_CONTRAST:
625 ctrl->value = cam->contrast;
626 break;
627 case V4L2_CID_SATURATION:
628 ctrl->value = cam->color;
629 break;
630 case V4L2_CID_HUE:
631 ctrl->value = cam->hue;
632 break;
633 default:
634 ret = -EINVAL;
635 break;
636 }
637 return ret;
638}
639
640static int cam_s_ctrl(struct file *file, void *priv,
641 struct v4l2_control *ctrl)
642{
643 struct w9966 *cam = video_drvdata(file);
644 int ret = 0;
645
646 mutex_lock(&cam->lock);
647 switch (ctrl->id) {
648 case V4L2_CID_BRIGHTNESS:
649 cam->brightness = ctrl->value;
650 break;
651 case V4L2_CID_CONTRAST:
652 cam->contrast = ctrl->value;
653 break;
654 case V4L2_CID_SATURATION:
655 cam->color = ctrl->value;
656 break;
657 case V4L2_CID_HUE:
658 cam->hue = ctrl->value;
659 break;
660 default:
661 ret = -EINVAL;
662 break;
663 }
664
665 if (ret == 0) {
666 w9966_pdev_claim(cam);
667
668 if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 ||
669 w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 ||
670 w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 ||
671 w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) {
672 ret = -EIO;
673 }
674
675 w9966_pdev_release(cam);
676 }
677 mutex_unlock(&cam->lock);
678 return ret;
679}
680
681static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
682{
683 struct w9966 *cam = video_drvdata(file);
684 struct v4l2_pix_format *pix = &fmt->fmt.pix;
685
686 pix->width = cam->width;
687 pix->height = cam->height;
688 pix->pixelformat = V4L2_PIX_FMT_YUYV;
689 pix->field = V4L2_FIELD_NONE;
690 pix->bytesperline = 2 * cam->width;
691 pix->sizeimage = 2 * cam->width * cam->height;
692 /* Just a guess */
693 pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
694 return 0;
695}
696
697static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
698{
699 struct v4l2_pix_format *pix = &fmt->fmt.pix;
700
701 if (pix->width < 2)
702 pix->width = 2;
703 if (pix->height < 1)
704 pix->height = 1;
705 if (pix->width > W9966_WND_MAX_W)
706 pix->width = W9966_WND_MAX_W;
707 if (pix->height > W9966_WND_MAX_H)
708 pix->height = W9966_WND_MAX_H;
709 pix->pixelformat = V4L2_PIX_FMT_YUYV;
710 pix->field = V4L2_FIELD_NONE;
711 pix->bytesperline = 2 * pix->width;
712 pix->sizeimage = 2 * pix->width * pix->height;
713 /* Just a guess */
714 pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
715 return 0;
716}
717
718static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
719{
720 struct w9966 *cam = video_drvdata(file);
721 struct v4l2_pix_format *pix = &fmt->fmt.pix;
722 int ret = cam_try_fmt_vid_cap(file, fh, fmt);
723
724 if (ret)
725 return ret;
726
727 mutex_lock(&cam->lock);
728 /* Update camera regs */
729 w9966_pdev_claim(cam);
730 ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height);
731 w9966_pdev_release(cam);
732 mutex_unlock(&cam->lock);
733 return ret;
734}
735
736static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
737{
738 static struct v4l2_fmtdesc formats[] = {
739 { 0, 0, 0,
740 "YUV 4:2:2", V4L2_PIX_FMT_YUYV,
741 { 0, 0, 0, 0 }
742 },
743 };
744 enum v4l2_buf_type type = fmt->type;
745
746 if (fmt->index > 0)
747 return -EINVAL;
748
749 *fmt = formats[fmt->index];
750 fmt->type = type;
751 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752}
753
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300754/* Capture data */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755static ssize_t w9966_v4l_read(struct file *file, char __user *buf,
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300756 size_t count, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757{
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300758 struct w9966 *cam = video_drvdata(file);
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300759 unsigned char addr = 0xa0; /* ECP, read, CCD-transfer, 00000 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 unsigned char __user *dest = (unsigned char __user *)buf;
761 unsigned long dleft = count;
762 unsigned char *tbuf;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300763
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300764 /* Why would anyone want more than this?? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 if (count > cam->width * cam->height * 2)
766 return -EINVAL;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300767
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300768 mutex_lock(&cam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 w9966_pdev_claim(cam);
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300770 w9966_write_reg(cam, 0x00, 0x02); /* Reset ECP-FIFO buffer */
771 w9966_write_reg(cam, 0x00, 0x00); /* Return to normal operation */
772 w9966_write_reg(cam, 0x01, 0x98); /* Enable capture */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300774 /* write special capture-addr and negotiate into data transfer */
775 if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) ||
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300776 (parport_write(cam->pport, &addr, 1) != 1) ||
777 (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 w9966_pdev_release(cam);
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300779 mutex_unlock(&cam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 return -EFAULT;
781 }
782
783 tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL);
784 if (tbuf == NULL) {
785 count = -ENOMEM;
786 goto out;
787 }
788
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300789 while (dleft > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300791
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 if (parport_read(cam->pport, tbuf, tsize) < tsize) {
793 count = -EFAULT;
794 goto out;
795 }
796 if (copy_to_user(dest, tbuf, tsize) != 0) {
797 count = -EFAULT;
798 goto out;
799 }
800 dest += tsize;
801 dleft -= tsize;
802 }
803
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300804 w9966_write_reg(cam, 0x01, 0x18); /* Disable capture */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
806out:
807 kfree(tbuf);
808 w9966_pdev_release(cam);
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300809 mutex_unlock(&cam->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
811 return count;
812}
813
Hans Verkuil271922c2010-03-22 05:13:17 -0300814static const struct v4l2_file_operations w9966_fops = {
815 .owner = THIS_MODULE,
Hans Verkuil61df3c92010-11-14 10:09:38 -0300816 .unlocked_ioctl = video_ioctl2,
Hans Verkuil271922c2010-03-22 05:13:17 -0300817 .read = w9966_v4l_read,
818};
819
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300820static const struct v4l2_ioctl_ops w9966_ioctl_ops = {
821 .vidioc_querycap = cam_querycap,
822 .vidioc_g_input = cam_g_input,
823 .vidioc_s_input = cam_s_input,
824 .vidioc_enum_input = cam_enum_input,
825 .vidioc_queryctrl = cam_queryctrl,
826 .vidioc_g_ctrl = cam_g_ctrl,
827 .vidioc_s_ctrl = cam_s_ctrl,
828 .vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap,
829 .vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap,
830 .vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap,
831 .vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap,
Hans Verkuil271922c2010-03-22 05:13:17 -0300832};
833
834
835/* Initialize camera device. Setup all internal flags, set a
836 default video mode, setup ccd-chip, register v4l device etc..
837 Also used for 'probing' of hardware.
838 -1 on error */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300839static int w9966_init(struct w9966 *cam, struct parport *port)
Hans Verkuil271922c2010-03-22 05:13:17 -0300840{
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300841 struct v4l2_device *v4l2_dev = &cam->v4l2_dev;
842
Hans Verkuil271922c2010-03-22 05:13:17 -0300843 if (cam->dev_state != 0)
844 return -1;
845
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300846 strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name));
847
848 if (v4l2_device_register(NULL, v4l2_dev) < 0) {
849 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
850 return -1;
851 }
Hans Verkuil271922c2010-03-22 05:13:17 -0300852 cam->pport = port;
853 cam->brightness = 128;
854 cam->contrast = 64;
855 cam->color = 64;
856 cam->hue = 0;
857
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300858 /* Select requested transfer mode */
Hans Verkuil271922c2010-03-22 05:13:17 -0300859 switch (parmode) {
860 default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */
861 case 0:
862 if (port->modes & PARPORT_MODE_ECP)
863 cam->ppmode = IEEE1284_MODE_ECP;
864 else if (port->modes & PARPORT_MODE_EPP)
865 cam->ppmode = IEEE1284_MODE_EPP;
866 else
867 cam->ppmode = IEEE1284_MODE_ECP;
868 break;
869 case 1: /* hw- or sw-ecp */
870 cam->ppmode = IEEE1284_MODE_ECP;
871 break;
872 case 2: /* hw- or sw-epp */
873 cam->ppmode = IEEE1284_MODE_EPP;
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300874 break;
Hans Verkuil271922c2010-03-22 05:13:17 -0300875 }
876
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300877 /* Tell the parport driver that we exists */
Hans Verkuil271922c2010-03-22 05:13:17 -0300878 cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL);
879 if (cam->pdev == NULL) {
880 DPRINTF("parport_register_device() failed\n");
881 return -1;
882 }
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300883 w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV);
Hans Verkuil271922c2010-03-22 05:13:17 -0300884
885 w9966_pdev_claim(cam);
886
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300887 /* Setup a default capture mode */
Hans Verkuil271922c2010-03-22 05:13:17 -0300888 if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) {
889 DPRINTF("w9966_setup() failed.\n");
890 return -1;
891 }
892
893 w9966_pdev_release(cam);
894
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300895 /* Fill in the video_device struct and register us to v4l */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300896 strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name));
897 cam->vdev.v4l2_dev = v4l2_dev;
898 cam->vdev.fops = &w9966_fops;
899 cam->vdev.ioctl_ops = &w9966_ioctl_ops;
900 cam->vdev.release = video_device_release_empty;
Hans Verkuil271922c2010-03-22 05:13:17 -0300901 video_set_drvdata(&cam->vdev, cam);
902
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300903 mutex_init(&cam->lock);
904
Hans Verkuil271922c2010-03-22 05:13:17 -0300905 if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
906 return -1;
907
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300908 w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV);
Hans Verkuil271922c2010-03-22 05:13:17 -0300909
910 /* All ok */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300911 v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n",
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300912 cam->pport->name);
Hans Verkuil271922c2010-03-22 05:13:17 -0300913 return 0;
914}
915
916
917/* Terminate everything gracefully */
Hans Verkuil626e2ac2010-04-06 11:36:39 -0300918static void w9966_term(struct w9966 *cam)
Hans Verkuil271922c2010-03-22 05:13:17 -0300919{
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300920 /* Unregister from v4l */
921 if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) {
Hans Verkuil271922c2010-03-22 05:13:17 -0300922 video_unregister_device(&cam->vdev);
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300923 w9966_set_state(cam, W9966_STATE_VDEV, 0);
Hans Verkuil271922c2010-03-22 05:13:17 -0300924 }
925
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300926 /* Terminate from IEEE1284 mode and release pdev block */
927 if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
Hans Verkuil271922c2010-03-22 05:13:17 -0300928 w9966_pdev_claim(cam);
929 parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT);
930 w9966_pdev_release(cam);
931 }
932
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300933 /* Unregister from parport */
934 if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
Hans Verkuil271922c2010-03-22 05:13:17 -0300935 parport_unregister_device(cam->pdev);
Hans Verkuilc1f2b0f2010-03-22 05:15:14 -0300936 w9966_set_state(cam, W9966_STATE_PDEV, 0);
Hans Verkuil271922c2010-03-22 05:13:17 -0300937 }
Hans Verkuilbd5ba3b2010-12-31 11:27:38 -0300938 memset(cam, 0, sizeof(*cam));
Hans Verkuil271922c2010-03-22 05:13:17 -0300939}
940
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300942/* Called once for every parport on init */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943static void w9966_attach(struct parport *port)
944{
945 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300946
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300947 for (i = 0; i < W9966_MAXCAMS; i++) {
948 if (w9966_cams[i].dev_state != 0) /* Cam is already assigned */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 continue;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300950 if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 if (w9966_init(&w9966_cams[i], port) != 0)
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300952 w9966_term(&w9966_cams[i]);
953 break; /* return */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 }
955 }
956}
957
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300958/* Called once for every parport on termination */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959static void w9966_detach(struct parport *port)
960{
961 int i;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300962
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 for (i = 0; i < W9966_MAXCAMS; i++)
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300964 if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port)
965 w9966_term(&w9966_cams[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966}
967
968
969static struct parport_driver w9966_ppd = {
970 .name = W9966_DRIVERNAME,
971 .attach = w9966_attach,
972 .detach = w9966_detach,
973};
974
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300975/* Module entry point */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976static int __init w9966_mod_init(void)
977{
978 int i;
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300979
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 for (i = 0; i < W9966_MAXCAMS; i++)
981 w9966_cams[i].dev_state = 0;
982
983 return parport_register_driver(&w9966_ppd);
984}
985
Hans Verkuil4bfdd582010-03-22 04:47:27 -0300986/* Module cleanup */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987static void __exit w9966_mod_term(void)
988{
989 parport_unregister_driver(&w9966_ppd);
990}
991
992module_init(w9966_mod_init);
993module_exit(w9966_mod_term);