blob: 2919aa42c94e6b92457569384aa333e4c58f091c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Media Vision Pro Movie Studio
3 * or
4 * "all you need is an I2C bus some RAM and a prayer"
5 *
6 * This draws heavily on code
7 *
8 * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994
9 * Kiefernring 15
10 * 14478 Potsdam, Germany
11 *
12 * Most of this code is directly derived from his userspace driver.
Alan Coxd9b01442008-10-27 15:13:47 -030013 * His driver works so send any reports to alan@lxorguk.ukuu.org.uk
14 * unless the userspace driver also doesn't work for you...
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030015 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 * Changes:
17 * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030018 * - pms_capture: report back -EFAULT
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 */
20
21#include <linux/module.h>
22#include <linux/delay.h>
23#include <linux/errno.h>
24#include <linux/fs.h>
25#include <linux/kernel.h>
26#include <linux/slab.h>
27#include <linux/mm.h>
28#include <linux/ioport.h>
29#include <linux/init.h>
30#include <asm/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/videodev.h>
Mauro Carvalho Chehab5e87efa2006-06-05 10:26:32 -030032#include <media/v4l2-common.h>
Hans Verkuil35ea11f2008-07-20 08:12:02 -030033#include <media/v4l2-ioctl.h>
Hans Verkuil1ce79812009-11-25 12:37:00 -030034#include <media/v4l2-device.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020035#include <linux/mutex.h>
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <asm/uaccess.h>
38
Hans Verkuil1ce79812009-11-25 12:37:00 -030039MODULE_LICENSE("GPL");
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42#define MOTOROLA 1
43#define PHILIPS2 2
44#define PHILIPS1 3
45#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
46
Hans Verkuil1ce79812009-11-25 12:37:00 -030047struct i2c_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 u8 slave;
49 u8 sub;
50 u8 data;
51 u8 hits;
52};
53
Hans Verkuil1ce79812009-11-25 12:37:00 -030054struct pms {
55 struct v4l2_device v4l2_dev;
56 struct video_device vdev;
57 struct video_picture picture;
58 int height;
59 int width;
60 unsigned long in_use;
61 struct mutex lock;
62 int i2c_count;
63 struct i2c_info i2cinfo[64];
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
Hans Verkuil1ce79812009-11-25 12:37:00 -030065 int decoder;
66 int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
67 int io;
68 int data;
69 void __iomem *mem;
70};
71
72static struct pms pms_card;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74/*
75 * I/O ports and Shared Memory
76 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030077
Hans Verkuil1ce79812009-11-25 12:37:00 -030078static int io_port = 0x250;
79module_param(io_port, int, 0);
80
81static int mem_base = 0xc8000;
82module_param(mem_base, int, 0);
83
84static int video_nr = -1;
85module_param(video_nr, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030087
Hans Verkuil1ce79812009-11-25 12:37:00 -030088static inline void mvv_write(struct pms *dev, u8 index, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -070089{
Hans Verkuil1ce79812009-11-25 12:37:00 -030090 outw(index | (value << 8), dev->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091}
92
Hans Verkuil1ce79812009-11-25 12:37:00 -030093static inline u8 mvv_read(struct pms *dev, u8 index)
Linus Torvalds1da177e2005-04-16 15:20:36 -070094{
Hans Verkuil1ce79812009-11-25 12:37:00 -030095 outb(index, dev->io);
96 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097}
98
Hans Verkuil1ce79812009-11-25 12:37:00 -030099static int pms_i2c_stat(struct pms *dev, u8 slave)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300101 int counter = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300103
Hans Verkuil1ce79812009-11-25 12:37:00 -0300104 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300105
Hans Verkuil1ce79812009-11-25 12:37:00 -0300106 while ((inb(dev->data) & 0x01) == 0)
107 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 break;
109
Hans Verkuil1ce79812009-11-25 12:37:00 -0300110 while ((inb(dev->data) & 0x01) != 0)
111 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300113
Hans Verkuil1ce79812009-11-25 12:37:00 -0300114 outb(slave, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300115
Hans Verkuil1ce79812009-11-25 12:37:00 -0300116 counter = 0;
117 while ((inb(dev->data) & 0x01) == 0)
118 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 break;
120
Hans Verkuil1ce79812009-11-25 12:37:00 -0300121 while ((inb(dev->data) & 0x01) != 0)
122 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300124
Hans Verkuil1ce79812009-11-25 12:37:00 -0300125 for (i = 0; i < 12; i++) {
126 char st = inb(dev->data);
127
128 if ((st & 2) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 return -1;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300130 if ((st & 1) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 break;
132 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300133 outb(0x29, dev->io);
134 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135}
136
Hans Verkuil1ce79812009-11-25 12:37:00 -0300137static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300139 int skip = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 int count;
141 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300142
Hans Verkuil1ce79812009-11-25 12:37:00 -0300143 for (i = 0; i < dev->i2c_count; i++) {
144 if ((dev->i2cinfo[i].slave == slave) &&
145 (dev->i2cinfo[i].sub == sub)) {
146 if (dev->i2cinfo[i].data == data)
147 skip = 1;
148 dev->i2cinfo[i].data = data;
149 i = dev->i2c_count + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 }
151 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300152
Hans Verkuil1ce79812009-11-25 12:37:00 -0300153 if (i == dev->i2c_count && dev->i2c_count < 64) {
154 dev->i2cinfo[dev->i2c_count].slave = slave;
155 dev->i2cinfo[dev->i2c_count].sub = sub;
156 dev->i2cinfo[dev->i2c_count].data = data;
157 dev->i2c_count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300159
Hans Verkuil1ce79812009-11-25 12:37:00 -0300160 if (skip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 return 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300162
Hans Verkuil1ce79812009-11-25 12:37:00 -0300163 mvv_write(dev, 0x29, sub);
164 mvv_write(dev, 0x2A, data);
165 mvv_write(dev, 0x28, slave);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300166
Hans Verkuil1ce79812009-11-25 12:37:00 -0300167 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300168
Hans Verkuil1ce79812009-11-25 12:37:00 -0300169 count = 0;
170 while ((inb(dev->data) & 1) == 0)
171 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 break;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300173 while ((inb(dev->data) & 1) != 0)
174 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300176
Hans Verkuil1ce79812009-11-25 12:37:00 -0300177 count = inb(dev->data);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300178
Hans Verkuil1ce79812009-11-25 12:37:00 -0300179 if (count & 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 return -1;
181 return count;
182}
183
Hans Verkuil1ce79812009-11-25 12:37:00 -0300184static int pms_i2c_read(struct pms *dev, int slave, int sub)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300186 int i;
187
188 for (i = 0; i < dev->i2c_count; i++) {
189 if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
190 return dev->i2cinfo[i].data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 }
192 return 0;
193}
194
195
Hans Verkuil1ce79812009-11-25 12:37:00 -0300196static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300198 u8 tmp;
199
Hans Verkuil1ce79812009-11-25 12:37:00 -0300200 tmp = pms_i2c_read(dev, slave, sub);
201 tmp = (tmp & and) | or;
202 pms_i2c_write(dev, slave, sub, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203}
204
205/*
206 * Control functions
207 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300208
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
Hans Verkuil1ce79812009-11-25 12:37:00 -0300210static void pms_videosource(struct pms *dev, short source)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300212 mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213}
214
Hans Verkuil1ce79812009-11-25 12:37:00 -0300215static void pms_hue(struct pms *dev, short hue)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300217 switch (dev->decoder) {
218 case MOTOROLA:
219 pms_i2c_write(dev, 0x8a, 0x00, hue);
220 break;
221 case PHILIPS2:
222 pms_i2c_write(dev, 0x8a, 0x07, hue);
223 break;
224 case PHILIPS1:
225 pms_i2c_write(dev, 0x42, 0x07, hue);
226 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 }
228}
229
Hans Verkuil1ce79812009-11-25 12:37:00 -0300230static void pms_colour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300232 switch (dev->decoder) {
233 case MOTOROLA:
234 pms_i2c_write(dev, 0x8a, 0x00, colour);
235 break;
236 case PHILIPS1:
237 pms_i2c_write(dev, 0x42, 0x12, colour);
238 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 }
240}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300241
242
Hans Verkuil1ce79812009-11-25 12:37:00 -0300243static void pms_contrast(struct pms *dev, short contrast)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300245 switch (dev->decoder) {
246 case MOTOROLA:
247 pms_i2c_write(dev, 0x8a, 0x00, contrast);
248 break;
249 case PHILIPS1:
250 pms_i2c_write(dev, 0x42, 0x13, contrast);
251 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 }
253}
254
Hans Verkuil1ce79812009-11-25 12:37:00 -0300255static void pms_brightness(struct pms *dev, short brightness)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300257 switch (dev->decoder) {
258 case MOTOROLA:
259 pms_i2c_write(dev, 0x8a, 0x00, brightness);
260 pms_i2c_write(dev, 0x8a, 0x00, brightness);
261 pms_i2c_write(dev, 0x8a, 0x00, brightness);
262 break;
263 case PHILIPS1:
264 pms_i2c_write(dev, 0x42, 0x19, brightness);
265 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 }
267}
268
269
Hans Verkuil1ce79812009-11-25 12:37:00 -0300270static void pms_format(struct pms *dev, short format)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
272 int target;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300273
Hans Verkuil1ce79812009-11-25 12:37:00 -0300274 dev->standard = format;
275
276 if (dev->decoder == PHILIPS1)
277 target = 0x42;
278 else if (dev->decoder == PHILIPS2)
279 target = 0x8a;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 else
281 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300282
Hans Verkuil1ce79812009-11-25 12:37:00 -0300283 switch (format) {
284 case 0: /* Auto */
285 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
286 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
287 break;
288 case 1: /* NTSC */
289 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
290 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
291 break;
292 case 2: /* PAL */
293 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
294 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
295 break;
296 case 3: /* SECAM */
297 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
298 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
299 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 }
301}
302
303#ifdef FOR_FUTURE_EXPANSION
304
305/*
306 * These features of the PMS card are not currently exposes. They
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300307 * could become a private v4l ioctl for PMSCONFIG or somesuch if
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 * people need it. We also don't yet use the PMS interrupt.
309 */
310
Hans Verkuil1ce79812009-11-25 12:37:00 -0300311static void pms_hstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300313 switch (dev->decoder) {
314 case PHILIPS1:
315 pms_i2c_write(dev, 0x8a, 0x05, start);
316 pms_i2c_write(dev, 0x8a, 0x18, start);
317 break;
318 case PHILIPS2:
319 pms_i2c_write(dev, 0x42, 0x05, start);
320 pms_i2c_write(dev, 0x42, 0x18, start);
321 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 }
323}
324
325/*
326 * Bandpass filters
327 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300328
Hans Verkuil1ce79812009-11-25 12:37:00 -0300329static void pms_bandpass(struct pms *dev, short pass)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300331 if (dev->decoder == PHILIPS2)
332 pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
333 else if (dev->decoder == PHILIPS1)
334 pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335}
336
Hans Verkuil1ce79812009-11-25 12:37:00 -0300337static void pms_antisnow(struct pms *dev, short snow)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300339 if (dev->decoder == PHILIPS2)
340 pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
341 else if (dev->decoder == PHILIPS1)
342 pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343}
344
Hans Verkuil1ce79812009-11-25 12:37:00 -0300345static void pms_sharpness(struct pms *dev, short sharp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300347 if (dev->decoder == PHILIPS2)
348 pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
349 else if (dev->decoder == PHILIPS1)
350 pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351}
352
Hans Verkuil1ce79812009-11-25 12:37:00 -0300353static void pms_chromaagc(struct pms *dev, short agc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300355 if (dev->decoder == PHILIPS2)
356 pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
357 else if (dev->decoder == PHILIPS1)
358 pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359}
360
Hans Verkuil1ce79812009-11-25 12:37:00 -0300361static void pms_vertnoise(struct pms *dev, short noise)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300363 if (dev->decoder == PHILIPS2)
364 pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
365 else if (dev->decoder == PHILIPS1)
366 pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367}
368
Hans Verkuil1ce79812009-11-25 12:37:00 -0300369static void pms_forcecolour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300371 if (dev->decoder == PHILIPS2)
372 pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
373 else if (dev->decoder == PHILIPS1)
374 pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375}
376
Hans Verkuil1ce79812009-11-25 12:37:00 -0300377static void pms_antigamma(struct pms *dev, short gamma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300379 if (dev->decoder == PHILIPS2)
380 pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
381 else if (dev->decoder == PHILIPS1)
382 pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383}
384
Hans Verkuil1ce79812009-11-25 12:37:00 -0300385static void pms_prefilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300387 if (dev->decoder == PHILIPS2)
388 pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
389 else if (dev->decoder == PHILIPS1)
390 pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391}
392
Hans Verkuil1ce79812009-11-25 12:37:00 -0300393static void pms_hfilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300395 if (dev->decoder == PHILIPS2)
396 pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
397 else if (dev->decoder == PHILIPS1)
398 pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399}
400
Hans Verkuil1ce79812009-11-25 12:37:00 -0300401static void pms_vfilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300403 if (dev->decoder == PHILIPS2)
404 pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
405 else if (dev->decoder == PHILIPS1)
406 pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
Hans Verkuil1ce79812009-11-25 12:37:00 -0300409static void pms_killcolour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300411 if (dev->decoder == PHILIPS2) {
412 pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
413 pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
414 } else if (dev->decoder == PHILIPS1) {
415 pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
416 pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 }
418}
419
Hans Verkuil1ce79812009-11-25 12:37:00 -0300420static void pms_chromagain(struct pms *dev, short chroma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300422 if (dev->decoder == PHILIPS2)
423 pms_i2c_write(dev, 0x8a, 0x11, chroma);
424 else if (dev->decoder == PHILIPS1)
425 pms_i2c_write(dev, 0x42, 0x11, chroma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426}
427
428
Hans Verkuil1ce79812009-11-25 12:37:00 -0300429static void pms_spacialcompl(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300431 mvv_write(dev, 0x3b, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432}
433
Hans Verkuil1ce79812009-11-25 12:37:00 -0300434static void pms_spacialcomph(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300436 mvv_write(dev, 0x3a, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437}
438
Hans Verkuil1ce79812009-11-25 12:37:00 -0300439static void pms_vstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300441 mvv_write(dev, 0x16, start);
442 mvv_write(dev, 0x17, (start >> 8) & 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443}
444
445#endif
446
Hans Verkuil1ce79812009-11-25 12:37:00 -0300447static void pms_secamcross(struct pms *dev, short cross)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300449 if (dev->decoder == PHILIPS2)
450 pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
451 else if (dev->decoder == PHILIPS1)
452 pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453}
454
455
Hans Verkuil1ce79812009-11-25 12:37:00 -0300456static void pms_swsense(struct pms *dev, short sense)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300458 if (dev->decoder == PHILIPS2) {
459 pms_i2c_write(dev, 0x8a, 0x0a, sense);
460 pms_i2c_write(dev, 0x8a, 0x0b, sense);
461 } else if (dev->decoder == PHILIPS1) {
462 pms_i2c_write(dev, 0x42, 0x0a, sense);
463 pms_i2c_write(dev, 0x42, 0x0b, sense);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 }
465}
466
467
Hans Verkuil1ce79812009-11-25 12:37:00 -0300468static void pms_framerate(struct pms *dev, short frr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300470 int fps = (dev->standard == 1) ? 30 : 25;
471
472 if (frr == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 return;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300474 fps = fps/frr;
475 mvv_write(dev, 0x14, 0x80 | fps);
476 mvv_write(dev, 0x15, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477}
478
Hans Verkuil1ce79812009-11-25 12:37:00 -0300479static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300481 mvv_write(dev, 0x1c, deciden); /* Denominator */
482 mvv_write(dev, 0x1d, decinum); /* Numerator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483}
484
485/*
486 * Turn 16bit ratios into best small ratio the chipset can grok
487 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300488
Hans Verkuil1ce79812009-11-25 12:37:00 -0300489static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300491 /* Knock it down by / 5 once */
492 if (decinum % 5 == 0) {
493 deciden /= 5;
494 decinum /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 }
496 /*
497 * 3's
498 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300499 while (decinum % 3 == 0 && deciden % 3 == 0) {
500 deciden /= 3;
501 decinum /= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 }
503 /*
504 * 2's
505 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300506 while (decinum % 2 == 0 && deciden % 2 == 0) {
507 decinum /= 2;
508 deciden /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 }
510 /*
511 * Fudgyify
512 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300513 while (deciden > 32) {
514 deciden /= 2;
515 decinum = (decinum + 1) / 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300517 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 deciden--;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300519 pms_vert(dev, deciden, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520}
521
Hans Verkuil1ce79812009-11-25 12:37:00 -0300522static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300524 if (decinum <= 512) {
525 if (decinum % 5 == 0) {
526 decinum /= 5;
527 deciden /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300529 } else {
530 decinum = 512;
531 deciden = 640; /* 768 would be ideal */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300533
Hans Verkuil1ce79812009-11-25 12:37:00 -0300534 while (((decinum | deciden) & 1) == 0) {
535 decinum >>= 1;
536 deciden >>= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300538 while (deciden > 32) {
539 deciden >>= 1;
540 decinum = (decinum + 1) >> 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300542 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 deciden--;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300544
Hans Verkuil1ce79812009-11-25 12:37:00 -0300545 mvv_write(dev, 0x24, 0x80 | deciden);
546 mvv_write(dev, 0x25, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547}
548
Hans Verkuil1ce79812009-11-25 12:37:00 -0300549static void pms_resolution(struct pms *dev, short width, short height)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
551 int fg_height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300552
Hans Verkuil1ce79812009-11-25 12:37:00 -0300553 fg_height = height;
554 if (fg_height > 280)
555 fg_height = 280;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300556
Hans Verkuil1ce79812009-11-25 12:37:00 -0300557 mvv_write(dev, 0x18, fg_height);
558 mvv_write(dev, 0x19, fg_height >> 8);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300559
Hans Verkuil1ce79812009-11-25 12:37:00 -0300560 if (dev->standard == 1) {
561 mvv_write(dev, 0x1a, 0xfc);
562 mvv_write(dev, 0x1b, 0x00);
563 if (height > fg_height)
564 pms_vertdeci(dev, 240, 240);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300566 pms_vertdeci(dev, fg_height, 240);
567 } else {
568 mvv_write(dev, 0x1a, 0x1a);
569 mvv_write(dev, 0x1b, 0x01);
570 if (fg_height > 256)
571 pms_vertdeci(dev, 270, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300573 pms_vertdeci(dev, fg_height, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300575 mvv_write(dev, 0x12, 0);
576 mvv_write(dev, 0x13, MVVMEMORYWIDTH);
577 mvv_write(dev, 0x42, 0x00);
578 mvv_write(dev, 0x43, 0x00);
579 mvv_write(dev, 0x44, MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300580
Hans Verkuil1ce79812009-11-25 12:37:00 -0300581 mvv_write(dev, 0x22, width + 8);
582 mvv_write(dev, 0x23, (width + 8) >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Hans Verkuil1ce79812009-11-25 12:37:00 -0300584 if (dev->standard == 1)
585 pms_horzdeci(dev, width, 640);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300587 pms_horzdeci(dev, width + 8, 768);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
Hans Verkuil1ce79812009-11-25 12:37:00 -0300589 mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
590 mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
591 mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
592 mvv_write(dev, 0x32, 0x00);
593 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594}
595
596
597/*
598 * Set Input
599 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300600
Hans Verkuil1ce79812009-11-25 12:37:00 -0300601static void pms_vcrinput(struct pms *dev, short input)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300603 if (dev->decoder == PHILIPS2)
604 pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
605 else if (dev->decoder == PHILIPS1)
606 pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607}
608
609
Hans Verkuil1ce79812009-11-25 12:37:00 -0300610static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611{
612 int y;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300613 int dw = 2 * dev->width;
614 char tmp[dw + 32]; /* using a temp buffer is faster than direct */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 int cnt = 0;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300616 int len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 unsigned char r8 = 0x5; /* value for reg8 */
618
619 if (rgb555)
620 r8 |= 0x20; /* else use untranslated rgb = 565 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300621 mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
623/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300624
Hans Verkuil1ce79812009-11-25 12:37:00 -0300625 for (y = 0; y < dev->height; y++) {
626 writeb(0, dev->mem); /* synchronisiert neue Zeile */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300627
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 /*
629 * This is in truth a fifo, be very careful as if you
630 * forgot this odd things will occur 8)
631 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300632
Hans Verkuil1ce79812009-11-25 12:37:00 -0300633 memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 cnt -= dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300635 while (cnt <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 /*
637 * Don't copy too far
638 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300639 int dt = dw;
640 if (dt + len > count)
641 dt = count - len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 cnt += dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300643 if (copy_to_user(buf, tmp + 32, dt))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 return len ? len : -EFAULT;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300645 buf += dt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 len += dt;
647 }
648 }
649 return len;
650}
651
652
653/*
654 * Video4linux interfacing
655 */
656
Hans Verkuil069b7472008-12-30 07:04:34 -0300657static long pms_do_ioctl(struct file *file, unsigned int cmd, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300659 struct pms *dev = video_drvdata(file);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300660
Hans Verkuil1ce79812009-11-25 12:37:00 -0300661 switch (cmd) {
662 case VIDIOCGCAP: {
663 struct video_capability *b = arg;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300664
Hans Verkuil1ce79812009-11-25 12:37:00 -0300665 strcpy(b->name, "Mediavision PMS");
666 b->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
667 b->channels = 4;
668 b->audios = 0;
669 b->maxwidth = 640;
670 b->maxheight = 480;
671 b->minwidth = 16;
672 b->minheight = 16;
673 return 0;
674 }
675 case VIDIOCGCHAN: {
676 struct video_channel *v = arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677
Hans Verkuil1ce79812009-11-25 12:37:00 -0300678 if (v->channel < 0 || v->channel > 3)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 return -EINVAL;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300680 v->flags = 0;
681 v->tuners = 1;
682 /* Good question.. its composite or SVHS so.. */
683 v->type = VIDEO_TYPE_CAMERA;
684 switch (v->channel) {
685 case 0:
686 strcpy(v->name, "Composite");
687 break;
688 case 1:
689 strcpy(v->name, "SVideo");
690 break;
691 case 2:
692 strcpy(v->name, "Composite(VCR)");
693 break;
694 case 3:
695 strcpy(v->name, "SVideo(VCR)");
696 break;
697 }
698 return 0;
699 }
700 case VIDIOCSCHAN: {
701 struct video_channel *v = arg;
702
703 if (v->channel < 0 || v->channel > 3)
704 return -EINVAL;
705 mutex_lock(&dev->lock);
706 pms_videosource(dev, v->channel & 1);
707 pms_vcrinput(dev, v->channel >> 1);
708 mutex_unlock(&dev->lock);
709 return 0;
710 }
711 case VIDIOCGTUNER: {
712 struct video_tuner *v = arg;
713
714 if (v->tuner)
715 return -EINVAL;
716 strcpy(v->name, "Format");
717 v->rangelow = 0;
718 v->rangehigh = 0;
719 v->flags = VIDEO_TUNER_PAL | VIDEO_TUNER_NTSC | VIDEO_TUNER_SECAM;
720 switch (dev->standard) {
721 case 0:
722 v->mode = VIDEO_MODE_AUTO;
723 break;
724 case 1:
725 v->mode = VIDEO_MODE_NTSC;
726 break;
727 case 2:
728 v->mode = VIDEO_MODE_PAL;
729 break;
730 case 3:
731 v->mode = VIDEO_MODE_SECAM;
732 break;
733 }
734 return 0;
735 }
736 case VIDIOCSTUNER: {
737 struct video_tuner *v = arg;
738
739 if (v->tuner)
740 return -EINVAL;
741 mutex_lock(&dev->lock);
742 switch (v->mode) {
743 case VIDEO_MODE_AUTO:
744 pms_framerate(dev, 25);
745 pms_secamcross(dev, 0);
746 pms_format(dev, 0);
747 break;
748 case VIDEO_MODE_NTSC:
749 pms_framerate(dev, 30);
750 pms_secamcross(dev, 0);
751 pms_format(dev, 1);
752 break;
753 case VIDEO_MODE_PAL:
754 pms_framerate(dev, 25);
755 pms_secamcross(dev, 0);
756 pms_format(dev, 2);
757 break;
758 case VIDEO_MODE_SECAM:
759 pms_framerate(dev, 25);
760 pms_secamcross(dev, 1);
761 pms_format(dev, 2);
762 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 default:
Hans Verkuil1ce79812009-11-25 12:37:00 -0300764 mutex_unlock(&dev->lock);
765 return -EINVAL;
766 }
767 mutex_unlock(&dev->lock);
768 return 0;
769 }
770 case VIDIOCGPICT: {
771 struct video_picture *p = arg;
772
773 *p = dev->picture;
774 return 0;
775 }
776 case VIDIOCSPICT: {
777 struct video_picture *p = arg;
778
779 if (!((p->palette == VIDEO_PALETTE_RGB565 && p->depth == 16) ||
780 (p->palette == VIDEO_PALETTE_RGB555 && p->depth == 15)))
781 return -EINVAL;
782 dev->picture = *p;
783
784 /*
785 * Now load the card.
786 */
787
788 mutex_lock(&dev->lock);
789 pms_brightness(dev, p->brightness >> 8);
790 pms_hue(dev, p->hue >> 8);
791 pms_colour(dev, p->colour >> 8);
792 pms_contrast(dev, p->contrast >> 8);
793 mutex_unlock(&dev->lock);
794 return 0;
795 }
796 case VIDIOCSWIN: {
797 struct video_window *vw = arg;
798
799 if (vw->flags)
800 return -EINVAL;
801 if (vw->clipcount)
802 return -EINVAL;
803 if (vw->height < 16 || vw->height > 480)
804 return -EINVAL;
805 if (vw->width < 16 || vw->width > 640)
806 return -EINVAL;
807 dev->width = vw->width;
808 dev->height = vw->height;
809 mutex_lock(&dev->lock);
810 pms_resolution(dev, dev->width, dev->height);
811 /* Ok we figured out what to use from our wide choice */
812 mutex_unlock(&dev->lock);
813 return 0;
814 }
815 case VIDIOCGWIN: {
816 struct video_window *vw = arg;
817
818 memset(vw, 0, sizeof(*vw));
819 vw->width = dev->width;
820 vw->height = dev->height;
821 return 0;
822 }
823 case VIDIOCKEY:
824 return 0;
825 case VIDIOCCAPTURE:
826 case VIDIOCGFBUF:
827 case VIDIOCSFBUF:
828 case VIDIOCGFREQ:
829 case VIDIOCSFREQ:
830 case VIDIOCGAUDIO:
831 case VIDIOCSAUDIO:
832 return -EINVAL;
833 default:
834 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 }
836 return 0;
837}
838
Hans Verkuil069b7472008-12-30 07:04:34 -0300839static long pms_ioctl(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 unsigned int cmd, unsigned long arg)
841{
Hans Verkuilf473bf72008-11-01 08:25:11 -0300842 return video_usercopy(file, cmd, arg, pms_do_ioctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843}
844
845static ssize_t pms_read(struct file *file, char __user *buf,
846 size_t count, loff_t *ppos)
847{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300848 struct pms *dev = video_drvdata(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 int len;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300850
Hans Verkuil1ce79812009-11-25 12:37:00 -0300851 mutex_lock(&dev->lock);
852 len = pms_capture(dev, buf, (dev->picture.depth == 16) ? 0 : 1, count);
853 mutex_unlock(&dev->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 return len;
855}
856
Hans Verkuilbec43662008-12-30 06:58:20 -0300857static int pms_exclusive_open(struct file *file)
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300858{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300859 struct pms *dev = video_drvdata(file);
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300860
Hans Verkuil1ce79812009-11-25 12:37:00 -0300861 return test_and_set_bit(0, &dev->in_use) ? -EBUSY : 0;
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300862}
863
Hans Verkuilbec43662008-12-30 06:58:20 -0300864static int pms_exclusive_release(struct file *file)
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300865{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300866 struct pms *dev = video_drvdata(file);
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300867
Hans Verkuil1ce79812009-11-25 12:37:00 -0300868 clear_bit(0, &dev->in_use);
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300869 return 0;
870}
871
Hans Verkuilbec43662008-12-30 06:58:20 -0300872static const struct v4l2_file_operations pms_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 .owner = THIS_MODULE,
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300874 .open = pms_exclusive_open,
875 .release = pms_exclusive_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 .ioctl = pms_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 .read = pms_read,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878};
879
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
881/*
882 * Probe for and initialise the Mediavision PMS
883 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300884
Hans Verkuil1ce79812009-11-25 12:37:00 -0300885static int init_mediavision(struct pms *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886{
887 int id;
888 int idec, decst;
889 int i;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300890 static const unsigned char i2c_defs[] = {
891 0x4c, 0x30, 0x00, 0xe8,
892 0xb6, 0xe2, 0x00, 0x00,
893 0xff, 0xff, 0x00, 0x00,
894 0x00, 0x00, 0x78, 0x98,
895 0x00, 0x00, 0x00, 0x00,
896 0x34, 0x0a, 0xf4, 0xce,
897 0xe4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 };
899
Hans Verkuil1ce79812009-11-25 12:37:00 -0300900 dev->mem = ioremap(mem_base, 0x800);
901 if (!dev->mem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 return -ENOMEM;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300903
Hans Verkuil1ce79812009-11-25 12:37:00 -0300904 if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
905 printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
906 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 return -EBUSY;
908 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300909 if (!request_region(dev->io, 3, "Mediavision PMS")) {
910 printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
911 release_region(0x9a01, 1);
912 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 return -EBUSY;
914 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300915 outb(0xb8, 0x9a01); /* Unlock */
916 outb(dev->io >> 4, 0x9a01); /* Set IO port */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300917
918
Hans Verkuil1ce79812009-11-25 12:37:00 -0300919 id = mvv_read(dev, 3);
920 decst = pms_i2c_stat(dev, 0x43);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300921
Hans Verkuil1ce79812009-11-25 12:37:00 -0300922 if (decst != -1)
923 idec = 2;
924 else if (pms_i2c_stat(dev, 0xb9) != -1)
925 idec = 3;
926 else if (pms_i2c_stat(dev, 0x8b) != -1)
927 idec = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300928 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300929 idec = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
931 printk(KERN_INFO "PMS type is %d\n", idec);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300932 if (idec == 0) {
933 release_region(dev->io, 3);
934 release_region(0x9a01, 1);
935 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 return -ENODEV;
937 }
938
939 /*
940 * Ok we have a PMS of some sort
941 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300942
Hans Verkuil1ce79812009-11-25 12:37:00 -0300943 mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300944
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 /* Ok now load the defaults */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300946
Hans Verkuil1ce79812009-11-25 12:37:00 -0300947 for (i = 0; i < 0x19; i++) {
948 if (i2c_defs[i] == 0xff)
949 pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300951 pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300953
Hans Verkuil1ce79812009-11-25 12:37:00 -0300954 pms_i2c_write(dev, 0xb8, 0x00, 0x12);
955 pms_i2c_write(dev, 0xb8, 0x04, 0x00);
956 pms_i2c_write(dev, 0xb8, 0x07, 0x00);
957 pms_i2c_write(dev, 0xb8, 0x08, 0x00);
958 pms_i2c_write(dev, 0xb8, 0x09, 0xff);
959 pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
960 pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
961 pms_i2c_write(dev, 0xb8, 0x10, 0x03);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300962
Hans Verkuil1ce79812009-11-25 12:37:00 -0300963 mvv_write(dev, 0x01, 0x00);
964 mvv_write(dev, 0x05, 0xa0);
965 mvv_write(dev, 0x08, 0x25);
966 mvv_write(dev, 0x09, 0x00);
967 mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300968
Hans Verkuil1ce79812009-11-25 12:37:00 -0300969 mvv_write(dev, 0x10, 0x02);
970 mvv_write(dev, 0x1e, 0x0c);
971 mvv_write(dev, 0x1f, 0x03);
972 mvv_write(dev, 0x26, 0x06);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300973
Hans Verkuil1ce79812009-11-25 12:37:00 -0300974 mvv_write(dev, 0x2b, 0x00);
975 mvv_write(dev, 0x2c, 0x20);
976 mvv_write(dev, 0x2d, 0x00);
977 mvv_write(dev, 0x2f, 0x70);
978 mvv_write(dev, 0x32, 0x00);
979 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
980 mvv_write(dev, 0x34, 0x00);
981 mvv_write(dev, 0x35, 0x00);
982 mvv_write(dev, 0x3a, 0x80);
983 mvv_write(dev, 0x3b, 0x10);
984 mvv_write(dev, 0x20, 0x00);
985 mvv_write(dev, 0x21, 0x00);
986 mvv_write(dev, 0x30, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 return 0;
988}
989
990/*
991 * Initialization and module stuff
992 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300993
Rene Hermanb54ff932008-09-03 16:48:21 -0300994#ifndef MODULE
995static int enable;
996module_param(enable, int, 0);
997#endif
998
Hans Verkuil1ce79812009-11-25 12:37:00 -0300999static int __init pms_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000{
Hans Verkuil1ce79812009-11-25 12:37:00 -03001001 struct pms *dev = &pms_card;
1002 struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
1003 int res;
1004
1005 strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name));
1006
1007 v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001008
Rene Hermanb54ff932008-09-03 16:48:21 -03001009#ifndef MODULE
1010 if (!enable) {
Hans Verkuil1ce79812009-11-25 12:37:00 -03001011 v4l2_err(v4l2_dev,
1012 "PMS: not enabled, use pms.enable=1 to probe\n");
Rene Hermanb54ff932008-09-03 16:48:21 -03001013 return -ENODEV;
1014 }
1015#endif
1016
Hans Verkuil1ce79812009-11-25 12:37:00 -03001017 dev->decoder = PHILIPS2;
1018 dev->io = io_port;
1019 dev->data = io_port + 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001020
Hans Verkuil1ce79812009-11-25 12:37:00 -03001021 if (init_mediavision(dev)) {
1022 v4l2_err(v4l2_dev, "Board not found.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 return -ENODEV;
1024 }
Hans Verkuil1ce79812009-11-25 12:37:00 -03001025
1026 res = v4l2_device_register(NULL, v4l2_dev);
1027 if (res < 0) {
1028 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
1029 return res;
1030 }
1031
1032 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
1033 dev->vdev.v4l2_dev = v4l2_dev;
1034 dev->vdev.fops = &pms_fops;
1035 dev->vdev.release = video_device_release_empty;
1036 video_set_drvdata(&dev->vdev, dev);
1037 mutex_init(&dev->lock);
1038 dev->height = 240;
1039 dev->width = 320;
1040 pms_swsense(dev, 75);
1041 pms_resolution(dev, 320, 240);
1042 pms_videosource(dev, 0);
1043 pms_vcrinput(dev, 0);
1044 if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
1045 v4l2_device_unregister(&dev->v4l2_dev);
1046 release_region(dev->io, 3);
1047 release_region(0x9a01, 1);
1048 iounmap(dev->mem);
1049 return -EINVAL;
1050 }
1051 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052}
1053
Hans Verkuil1ce79812009-11-25 12:37:00 -03001054static void __exit pms_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055{
Hans Verkuil1ce79812009-11-25 12:37:00 -03001056 struct pms *dev = &pms_card;
1057
1058 video_unregister_device(&dev->vdev);
1059 release_region(dev->io, 3);
1060 release_region(0x9a01, 1);
1061 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062}
1063
Hans Verkuil1ce79812009-11-25 12:37:00 -03001064module_init(pms_init);
1065module_exit(pms_exit);