blob: e753b5e4d2ce449ba3730d3a3f17244491b9e04a [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:
Hans Verkuilfeba2f82009-11-25 12:47:02 -030017 * 25-11-2009 Hans Verkuil <hverkuil@xs4all.nl>
18 * - converted to version 2 of the V4L API.
19 * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
20 * - pms_capture: report back -EFAULT
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 */
22
23#include <linux/module.h>
24#include <linux/delay.h>
25#include <linux/errno.h>
26#include <linux/fs.h>
27#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/mm.h>
29#include <linux/ioport.h>
30#include <linux/init.h>
Hans Verkuilfeba2f82009-11-25 12:47:02 -030031#include <linux/mutex.h>
Mauro Carvalho Chehab262ab9a2009-12-14 16:43:13 -030032#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <asm/io.h>
Hans Verkuilfeba2f82009-11-25 12:47:02 -030034
35#include <linux/videodev2.h>
Mauro Carvalho Chehab5e87efa2006-06-05 10:26:32 -030036#include <media/v4l2-common.h>
Hans Verkuil35ea11f2008-07-20 08:12:02 -030037#include <media/v4l2-ioctl.h>
Hans Verkuil1ce79812009-11-25 12:37:00 -030038#include <media/v4l2-device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
Hans Verkuil1ce79812009-11-25 12:37:00 -030040MODULE_LICENSE("GPL");
Mauro Carvalho Chehab1990d502011-06-24 14:45:49 -030041MODULE_VERSION("0.0.4");
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
43#define MOTOROLA 1
Hans Verkuilfeba2f82009-11-25 12:47:02 -030044#define PHILIPS2 2 /* SAA7191 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#define PHILIPS1 3
46#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
47
Hans Verkuil1ce79812009-11-25 12:37:00 -030048struct i2c_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 u8 slave;
50 u8 sub;
51 u8 data;
52 u8 hits;
53};
54
Hans Verkuil1ce79812009-11-25 12:37:00 -030055struct pms {
56 struct v4l2_device v4l2_dev;
57 struct video_device vdev;
Hans Verkuil1ce79812009-11-25 12:37:00 -030058 int height;
59 int width;
Hans Verkuilfeba2f82009-11-25 12:47:02 -030060 int depth;
61 int input;
62 s32 brightness, saturation, hue, contrast;
Hans Verkuil1ce79812009-11-25 12:37:00 -030063 struct mutex lock;
64 int i2c_count;
65 struct i2c_info i2cinfo[64];
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Hans Verkuil1ce79812009-11-25 12:37:00 -030067 int decoder;
68 int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
Hans Verkuilfeba2f82009-11-25 12:47:02 -030069 v4l2_std_id std;
Hans Verkuil1ce79812009-11-25 12:37:00 -030070 int io;
71 int data;
72 void __iomem *mem;
73};
74
75static struct pms pms_card;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77/*
78 * I/O ports and Shared Memory
79 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030080
Hans Verkuil1ce79812009-11-25 12:37:00 -030081static int io_port = 0x250;
82module_param(io_port, int, 0);
83
84static int mem_base = 0xc8000;
85module_param(mem_base, int, 0);
86
87static int video_nr = -1;
88module_param(video_nr, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030090
Hans Verkuil1ce79812009-11-25 12:37:00 -030091static inline void mvv_write(struct pms *dev, u8 index, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -070092{
Hans Verkuil1ce79812009-11-25 12:37:00 -030093 outw(index | (value << 8), dev->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094}
95
Hans Verkuil1ce79812009-11-25 12:37:00 -030096static inline u8 mvv_read(struct pms *dev, u8 index)
Linus Torvalds1da177e2005-04-16 15:20:36 -070097{
Hans Verkuil1ce79812009-11-25 12:37:00 -030098 outb(index, dev->io);
99 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100}
101
Hans Verkuil1ce79812009-11-25 12:37:00 -0300102static int pms_i2c_stat(struct pms *dev, u8 slave)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300104 int counter = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300106
Hans Verkuil1ce79812009-11-25 12:37:00 -0300107 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300108
Hans Verkuil1ce79812009-11-25 12:37:00 -0300109 while ((inb(dev->data) & 0x01) == 0)
110 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 break;
112
Hans Verkuil1ce79812009-11-25 12:37:00 -0300113 while ((inb(dev->data) & 0x01) != 0)
114 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300116
Hans Verkuil1ce79812009-11-25 12:37:00 -0300117 outb(slave, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300118
Hans Verkuil1ce79812009-11-25 12:37:00 -0300119 counter = 0;
120 while ((inb(dev->data) & 0x01) == 0)
121 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 break;
123
Hans Verkuil1ce79812009-11-25 12:37:00 -0300124 while ((inb(dev->data) & 0x01) != 0)
125 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300127
Hans Verkuil1ce79812009-11-25 12:37:00 -0300128 for (i = 0; i < 12; i++) {
129 char st = inb(dev->data);
130
131 if ((st & 2) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 return -1;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300133 if ((st & 1) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 break;
135 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300136 outb(0x29, dev->io);
137 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138}
139
Hans Verkuil1ce79812009-11-25 12:37:00 -0300140static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300142 int skip = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 int count;
144 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300145
Hans Verkuil1ce79812009-11-25 12:37:00 -0300146 for (i = 0; i < dev->i2c_count; i++) {
147 if ((dev->i2cinfo[i].slave == slave) &&
148 (dev->i2cinfo[i].sub == sub)) {
149 if (dev->i2cinfo[i].data == data)
150 skip = 1;
151 dev->i2cinfo[i].data = data;
152 i = dev->i2c_count + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 }
154 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300155
Hans Verkuil1ce79812009-11-25 12:37:00 -0300156 if (i == dev->i2c_count && dev->i2c_count < 64) {
157 dev->i2cinfo[dev->i2c_count].slave = slave;
158 dev->i2cinfo[dev->i2c_count].sub = sub;
159 dev->i2cinfo[dev->i2c_count].data = data;
160 dev->i2c_count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300162
Hans Verkuil1ce79812009-11-25 12:37:00 -0300163 if (skip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 return 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300165
Hans Verkuil1ce79812009-11-25 12:37:00 -0300166 mvv_write(dev, 0x29, sub);
167 mvv_write(dev, 0x2A, data);
168 mvv_write(dev, 0x28, slave);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300169
Hans Verkuil1ce79812009-11-25 12:37:00 -0300170 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300171
Hans Verkuil1ce79812009-11-25 12:37:00 -0300172 count = 0;
173 while ((inb(dev->data) & 1) == 0)
174 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 break;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300176 while ((inb(dev->data) & 1) != 0)
177 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300179
Hans Verkuil1ce79812009-11-25 12:37:00 -0300180 count = inb(dev->data);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300181
Hans Verkuil1ce79812009-11-25 12:37:00 -0300182 if (count & 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 return -1;
184 return count;
185}
186
Hans Verkuil1ce79812009-11-25 12:37:00 -0300187static int pms_i2c_read(struct pms *dev, int slave, int sub)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300189 int i;
190
191 for (i = 0; i < dev->i2c_count; i++) {
192 if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
193 return dev->i2cinfo[i].data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 }
195 return 0;
196}
197
198
Hans Verkuil1ce79812009-11-25 12:37:00 -0300199static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300201 u8 tmp;
202
Hans Verkuil1ce79812009-11-25 12:37:00 -0300203 tmp = pms_i2c_read(dev, slave, sub);
204 tmp = (tmp & and) | or;
205 pms_i2c_write(dev, slave, sub, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206}
207
208/*
209 * Control functions
210 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300211
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
Hans Verkuil1ce79812009-11-25 12:37:00 -0300213static void pms_videosource(struct pms *dev, short source)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300215 switch (dev->decoder) {
216 case MOTOROLA:
217 break;
218 case PHILIPS2:
219 pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
220 break;
221 case PHILIPS1:
222 break;
223 }
224 mvv_write(dev, 0x2E, 0x31);
225 /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
226 But could not make this work correctly. Only Composite input
227 worked for me. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228}
229
Hans Verkuil1ce79812009-11-25 12:37:00 -0300230static void pms_hue(struct pms *dev, short hue)
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, hue);
235 break;
236 case PHILIPS2:
237 pms_i2c_write(dev, 0x8a, 0x07, hue);
238 break;
239 case PHILIPS1:
240 pms_i2c_write(dev, 0x42, 0x07, hue);
241 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 }
243}
244
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300245static void pms_saturation(struct pms *dev, short sat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300247 switch (dev->decoder) {
248 case MOTOROLA:
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300249 pms_i2c_write(dev, 0x8a, 0x00, sat);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300250 break;
251 case PHILIPS1:
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300252 pms_i2c_write(dev, 0x42, 0x12, sat);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300253 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 }
255}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300256
257
Hans Verkuil1ce79812009-11-25 12:37:00 -0300258static void pms_contrast(struct pms *dev, short contrast)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300260 switch (dev->decoder) {
261 case MOTOROLA:
262 pms_i2c_write(dev, 0x8a, 0x00, contrast);
263 break;
264 case PHILIPS1:
265 pms_i2c_write(dev, 0x42, 0x13, contrast);
266 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 }
268}
269
Hans Verkuil1ce79812009-11-25 12:37:00 -0300270static void pms_brightness(struct pms *dev, short brightness)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300272 switch (dev->decoder) {
273 case MOTOROLA:
274 pms_i2c_write(dev, 0x8a, 0x00, brightness);
275 pms_i2c_write(dev, 0x8a, 0x00, brightness);
276 pms_i2c_write(dev, 0x8a, 0x00, brightness);
277 break;
278 case PHILIPS1:
279 pms_i2c_write(dev, 0x42, 0x19, brightness);
280 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 }
282}
283
284
Hans Verkuil1ce79812009-11-25 12:37:00 -0300285static void pms_format(struct pms *dev, short format)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
287 int target;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300288
Hans Verkuil1ce79812009-11-25 12:37:00 -0300289 dev->standard = format;
290
291 if (dev->decoder == PHILIPS1)
292 target = 0x42;
293 else if (dev->decoder == PHILIPS2)
294 target = 0x8a;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 else
296 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300297
Hans Verkuil1ce79812009-11-25 12:37:00 -0300298 switch (format) {
299 case 0: /* Auto */
300 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
301 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
302 break;
303 case 1: /* NTSC */
304 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
305 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
306 break;
307 case 2: /* PAL */
308 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
309 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
310 break;
311 case 3: /* SECAM */
312 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
313 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
314 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 }
316}
317
318#ifdef FOR_FUTURE_EXPANSION
319
320/*
321 * These features of the PMS card are not currently exposes. They
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300322 * could become a private v4l ioctl for PMSCONFIG or somesuch if
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 * people need it. We also don't yet use the PMS interrupt.
324 */
325
Hans Verkuil1ce79812009-11-25 12:37:00 -0300326static void pms_hstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300328 switch (dev->decoder) {
329 case PHILIPS1:
330 pms_i2c_write(dev, 0x8a, 0x05, start);
331 pms_i2c_write(dev, 0x8a, 0x18, start);
332 break;
333 case PHILIPS2:
334 pms_i2c_write(dev, 0x42, 0x05, start);
335 pms_i2c_write(dev, 0x42, 0x18, start);
336 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 }
338}
339
340/*
341 * Bandpass filters
342 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300343
Hans Verkuil1ce79812009-11-25 12:37:00 -0300344static void pms_bandpass(struct pms *dev, short pass)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300346 if (dev->decoder == PHILIPS2)
347 pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
348 else if (dev->decoder == PHILIPS1)
349 pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350}
351
Hans Verkuil1ce79812009-11-25 12:37:00 -0300352static void pms_antisnow(struct pms *dev, short snow)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300354 if (dev->decoder == PHILIPS2)
355 pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
356 else if (dev->decoder == PHILIPS1)
357 pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358}
359
Hans Verkuil1ce79812009-11-25 12:37:00 -0300360static void pms_sharpness(struct pms *dev, short sharp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300362 if (dev->decoder == PHILIPS2)
363 pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
364 else if (dev->decoder == PHILIPS1)
365 pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366}
367
Hans Verkuil1ce79812009-11-25 12:37:00 -0300368static void pms_chromaagc(struct pms *dev, short agc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300370 if (dev->decoder == PHILIPS2)
371 pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
372 else if (dev->decoder == PHILIPS1)
373 pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374}
375
Hans Verkuil1ce79812009-11-25 12:37:00 -0300376static void pms_vertnoise(struct pms *dev, short noise)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300378 if (dev->decoder == PHILIPS2)
379 pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
380 else if (dev->decoder == PHILIPS1)
381 pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382}
383
Hans Verkuil1ce79812009-11-25 12:37:00 -0300384static void pms_forcecolour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300386 if (dev->decoder == PHILIPS2)
387 pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
388 else if (dev->decoder == PHILIPS1)
389 pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390}
391
Hans Verkuil1ce79812009-11-25 12:37:00 -0300392static void pms_antigamma(struct pms *dev, short gamma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300394 if (dev->decoder == PHILIPS2)
395 pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
396 else if (dev->decoder == PHILIPS1)
397 pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398}
399
Hans Verkuil1ce79812009-11-25 12:37:00 -0300400static void pms_prefilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300402 if (dev->decoder == PHILIPS2)
403 pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
404 else if (dev->decoder == PHILIPS1)
405 pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406}
407
Hans Verkuil1ce79812009-11-25 12:37:00 -0300408static void pms_hfilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300410 if (dev->decoder == PHILIPS2)
411 pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
412 else if (dev->decoder == PHILIPS1)
413 pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414}
415
Hans Verkuil1ce79812009-11-25 12:37:00 -0300416static void pms_vfilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300418 if (dev->decoder == PHILIPS2)
419 pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
420 else if (dev->decoder == PHILIPS1)
421 pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422}
423
Hans Verkuil1ce79812009-11-25 12:37:00 -0300424static void pms_killcolour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300426 if (dev->decoder == PHILIPS2) {
427 pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
428 pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
429 } else if (dev->decoder == PHILIPS1) {
430 pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
431 pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 }
433}
434
Hans Verkuil1ce79812009-11-25 12:37:00 -0300435static void pms_chromagain(struct pms *dev, short chroma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300437 if (dev->decoder == PHILIPS2)
438 pms_i2c_write(dev, 0x8a, 0x11, chroma);
439 else if (dev->decoder == PHILIPS1)
440 pms_i2c_write(dev, 0x42, 0x11, chroma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441}
442
443
Hans Verkuil1ce79812009-11-25 12:37:00 -0300444static void pms_spacialcompl(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300446 mvv_write(dev, 0x3b, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447}
448
Hans Verkuil1ce79812009-11-25 12:37:00 -0300449static void pms_spacialcomph(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300451 mvv_write(dev, 0x3a, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452}
453
Hans Verkuil1ce79812009-11-25 12:37:00 -0300454static void pms_vstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300456 mvv_write(dev, 0x16, start);
457 mvv_write(dev, 0x17, (start >> 8) & 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458}
459
460#endif
461
Hans Verkuil1ce79812009-11-25 12:37:00 -0300462static void pms_secamcross(struct pms *dev, short cross)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300464 if (dev->decoder == PHILIPS2)
465 pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
466 else if (dev->decoder == PHILIPS1)
467 pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468}
469
470
Hans Verkuil1ce79812009-11-25 12:37:00 -0300471static void pms_swsense(struct pms *dev, short sense)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300473 if (dev->decoder == PHILIPS2) {
474 pms_i2c_write(dev, 0x8a, 0x0a, sense);
475 pms_i2c_write(dev, 0x8a, 0x0b, sense);
476 } else if (dev->decoder == PHILIPS1) {
477 pms_i2c_write(dev, 0x42, 0x0a, sense);
478 pms_i2c_write(dev, 0x42, 0x0b, sense);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 }
480}
481
482
Hans Verkuil1ce79812009-11-25 12:37:00 -0300483static void pms_framerate(struct pms *dev, short frr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300485 int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300486
487 if (frr == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 return;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300489 fps = fps/frr;
490 mvv_write(dev, 0x14, 0x80 | fps);
491 mvv_write(dev, 0x15, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492}
493
Hans Verkuil1ce79812009-11-25 12:37:00 -0300494static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300496 mvv_write(dev, 0x1c, deciden); /* Denominator */
497 mvv_write(dev, 0x1d, decinum); /* Numerator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
499
500/*
501 * Turn 16bit ratios into best small ratio the chipset can grok
502 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300503
Hans Verkuil1ce79812009-11-25 12:37:00 -0300504static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300506 /* Knock it down by / 5 once */
507 if (decinum % 5 == 0) {
508 deciden /= 5;
509 decinum /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 }
511 /*
512 * 3's
513 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300514 while (decinum % 3 == 0 && deciden % 3 == 0) {
515 deciden /= 3;
516 decinum /= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 }
518 /*
519 * 2's
520 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300521 while (decinum % 2 == 0 && deciden % 2 == 0) {
522 decinum /= 2;
523 deciden /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 }
525 /*
526 * Fudgyify
527 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300528 while (deciden > 32) {
529 deciden /= 2;
530 decinum = (decinum + 1) / 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300532 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 deciden--;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300534 pms_vert(dev, deciden, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535}
536
Hans Verkuil1ce79812009-11-25 12:37:00 -0300537static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300539 if (decinum <= 512) {
540 if (decinum % 5 == 0) {
541 decinum /= 5;
542 deciden /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300544 } else {
545 decinum = 512;
546 deciden = 640; /* 768 would be ideal */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300548
Hans Verkuil1ce79812009-11-25 12:37:00 -0300549 while (((decinum | deciden) & 1) == 0) {
550 decinum >>= 1;
551 deciden >>= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300553 while (deciden > 32) {
554 deciden >>= 1;
555 decinum = (decinum + 1) >> 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300557 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 deciden--;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300559
Hans Verkuil1ce79812009-11-25 12:37:00 -0300560 mvv_write(dev, 0x24, 0x80 | deciden);
561 mvv_write(dev, 0x25, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562}
563
Hans Verkuil1ce79812009-11-25 12:37:00 -0300564static void pms_resolution(struct pms *dev, short width, short height)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
566 int fg_height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300567
Hans Verkuil1ce79812009-11-25 12:37:00 -0300568 fg_height = height;
569 if (fg_height > 280)
570 fg_height = 280;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300571
Hans Verkuil1ce79812009-11-25 12:37:00 -0300572 mvv_write(dev, 0x18, fg_height);
573 mvv_write(dev, 0x19, fg_height >> 8);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300574
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300575 if (dev->std & V4L2_STD_525_60) {
Hans Verkuil1ce79812009-11-25 12:37:00 -0300576 mvv_write(dev, 0x1a, 0xfc);
577 mvv_write(dev, 0x1b, 0x00);
578 if (height > fg_height)
579 pms_vertdeci(dev, 240, 240);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300581 pms_vertdeci(dev, fg_height, 240);
582 } else {
583 mvv_write(dev, 0x1a, 0x1a);
584 mvv_write(dev, 0x1b, 0x01);
585 if (fg_height > 256)
586 pms_vertdeci(dev, 270, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300588 pms_vertdeci(dev, fg_height, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300590 mvv_write(dev, 0x12, 0);
591 mvv_write(dev, 0x13, MVVMEMORYWIDTH);
592 mvv_write(dev, 0x42, 0x00);
593 mvv_write(dev, 0x43, 0x00);
594 mvv_write(dev, 0x44, MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300595
Hans Verkuil1ce79812009-11-25 12:37:00 -0300596 mvv_write(dev, 0x22, width + 8);
597 mvv_write(dev, 0x23, (width + 8) >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300599 if (dev->std & V4L2_STD_525_60)
Hans Verkuil1ce79812009-11-25 12:37:00 -0300600 pms_horzdeci(dev, width, 640);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300602 pms_horzdeci(dev, width + 8, 768);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Hans Verkuil1ce79812009-11-25 12:37:00 -0300604 mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
605 mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
606 mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
607 mvv_write(dev, 0x32, 0x00);
608 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609}
610
611
612/*
613 * Set Input
614 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300615
Hans Verkuil1ce79812009-11-25 12:37:00 -0300616static void pms_vcrinput(struct pms *dev, short input)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300618 if (dev->decoder == PHILIPS2)
619 pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
620 else if (dev->decoder == PHILIPS1)
621 pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622}
623
624
Hans Verkuil1ce79812009-11-25 12:37:00 -0300625static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626{
627 int y;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300628 int dw = 2 * dev->width;
629 char tmp[dw + 32]; /* using a temp buffer is faster than direct */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 int cnt = 0;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300631 int len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 unsigned char r8 = 0x5; /* value for reg8 */
633
634 if (rgb555)
635 r8 |= 0x20; /* else use untranslated rgb = 565 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300636 mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
638/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300639
Hans Verkuil1ce79812009-11-25 12:37:00 -0300640 for (y = 0; y < dev->height; y++) {
641 writeb(0, dev->mem); /* synchronisiert neue Zeile */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300642
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 /*
644 * This is in truth a fifo, be very careful as if you
645 * forgot this odd things will occur 8)
646 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300647
Hans Verkuil1ce79812009-11-25 12:37:00 -0300648 memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 cnt -= dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300650 while (cnt <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 /*
652 * Don't copy too far
653 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300654 int dt = dw;
655 if (dt + len > count)
656 dt = count - len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 cnt += dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300658 if (copy_to_user(buf, tmp + 32, dt))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 return len ? len : -EFAULT;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300660 buf += dt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 len += dt;
662 }
663 }
664 return len;
665}
666
667
668/*
669 * Video4linux interfacing
670 */
671
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300672static int pms_querycap(struct file *file, void *priv,
673 struct v4l2_capability *vcap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300675 struct pms *dev = video_drvdata(file);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300676
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300677 strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
678 strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
679 strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info));
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300680 vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 return 0;
682}
683
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300684static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300686 static const char *inputs[4] = {
687 "Composite",
688 "S-Video",
689 "Composite (VCR)",
690 "S-Video (VCR)"
691 };
692
693 if (vin->index > 3)
694 return -EINVAL;
695 strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
696 vin->type = V4L2_INPUT_TYPE_CAMERA;
697 vin->audioset = 0;
698 vin->tuner = 0;
699 vin->std = V4L2_STD_ALL;
700 vin->status = 0;
701 return 0;
702}
703
704static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
705{
706 struct pms *dev = video_drvdata(file);
707
708 *inp = dev->input;
709 return 0;
710}
711
712static int pms_s_input(struct file *file, void *fh, unsigned int inp)
713{
714 struct pms *dev = video_drvdata(file);
715
716 if (inp > 3)
717 return -EINVAL;
718
719 mutex_lock(&dev->lock);
720 dev->input = inp;
721 pms_videosource(dev, inp & 1);
722 pms_vcrinput(dev, inp >> 1);
723 mutex_unlock(&dev->lock);
724 return 0;
725}
726
727static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
728{
729 struct pms *dev = video_drvdata(file);
730
731 *std = dev->std;
732 return 0;
733}
734
735static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std)
736{
737 struct pms *dev = video_drvdata(file);
738 int ret = 0;
739
740 dev->std = *std;
741 mutex_lock(&dev->lock);
742 if (dev->std & V4L2_STD_NTSC) {
743 pms_framerate(dev, 30);
744 pms_secamcross(dev, 0);
745 pms_format(dev, 1);
746 } else if (dev->std & V4L2_STD_PAL) {
747 pms_framerate(dev, 25);
748 pms_secamcross(dev, 0);
749 pms_format(dev, 2);
750 } else if (dev->std & V4L2_STD_SECAM) {
751 pms_framerate(dev, 25);
752 pms_secamcross(dev, 1);
753 pms_format(dev, 2);
754 } else {
755 ret = -EINVAL;
756 }
757 /*
758 switch (v->mode) {
759 case VIDEO_MODE_AUTO:
760 pms_framerate(dev, 25);
761 pms_secamcross(dev, 0);
762 pms_format(dev, 0);
763 break;
764 }*/
765 mutex_unlock(&dev->lock);
766 return 0;
767}
768
769static int pms_queryctrl(struct file *file, void *priv,
770 struct v4l2_queryctrl *qc)
771{
772 switch (qc->id) {
773 case V4L2_CID_BRIGHTNESS:
774 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139);
775 case V4L2_CID_CONTRAST:
776 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70);
777 case V4L2_CID_SATURATION:
778 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64);
779 case V4L2_CID_HUE:
780 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
781 }
782 return -EINVAL;
783}
784
785static int pms_g_ctrl(struct file *file, void *priv,
786 struct v4l2_control *ctrl)
787{
788 struct pms *dev = video_drvdata(file);
789 int ret = 0;
790
791 switch (ctrl->id) {
792 case V4L2_CID_BRIGHTNESS:
793 ctrl->value = dev->brightness;
794 break;
795 case V4L2_CID_CONTRAST:
796 ctrl->value = dev->contrast;
797 break;
798 case V4L2_CID_SATURATION:
799 ctrl->value = dev->saturation;
800 break;
801 case V4L2_CID_HUE:
802 ctrl->value = dev->hue;
803 break;
804 default:
805 ret = -EINVAL;
806 break;
807 }
808 return ret;
809}
810
811static int pms_s_ctrl(struct file *file, void *priv,
812 struct v4l2_control *ctrl)
813{
814 struct pms *dev = video_drvdata(file);
815 int ret = 0;
816
817 mutex_lock(&dev->lock);
818 switch (ctrl->id) {
819 case V4L2_CID_BRIGHTNESS:
820 dev->brightness = ctrl->value;
821 pms_brightness(dev, dev->brightness);
822 break;
823 case V4L2_CID_CONTRAST:
824 dev->contrast = ctrl->value;
825 pms_contrast(dev, dev->contrast);
826 break;
827 case V4L2_CID_SATURATION:
828 dev->saturation = ctrl->value;
829 pms_saturation(dev, dev->saturation);
830 break;
831 case V4L2_CID_HUE:
832 dev->hue = ctrl->value;
833 pms_hue(dev, dev->hue);
834 break;
835 default:
836 ret = -EINVAL;
837 break;
838 }
839 mutex_unlock(&dev->lock);
840 return ret;
841}
842
843static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
844{
845 struct pms *dev = video_drvdata(file);
846 struct v4l2_pix_format *pix = &fmt->fmt.pix;
847
848 pix->width = dev->width;
849 pix->height = dev->height;
850 pix->pixelformat = dev->width == 15 ?
851 V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
852 pix->field = V4L2_FIELD_NONE;
853 pix->bytesperline = 2 * dev->width;
854 pix->sizeimage = 2 * dev->width * dev->height;
855 /* Just a guess */
856 pix->colorspace = V4L2_COLORSPACE_SRGB;
857 return 0;
858}
859
860static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
861{
862 struct v4l2_pix_format *pix = &fmt->fmt.pix;
863
864 if (pix->height < 16 || pix->height > 480)
865 return -EINVAL;
866 if (pix->width < 16 || pix->width > 640)
867 return -EINVAL;
868 if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
869 pix->pixelformat != V4L2_PIX_FMT_RGB565)
870 return -EINVAL;
871 pix->field = V4L2_FIELD_NONE;
872 pix->bytesperline = 2 * pix->width;
873 pix->sizeimage = 2 * pix->width * pix->height;
874 /* Just a guess */
875 pix->colorspace = V4L2_COLORSPACE_SRGB;
876 return 0;
877}
878
879static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
880{
881 struct pms *dev = video_drvdata(file);
882 struct v4l2_pix_format *pix = &fmt->fmt.pix;
883 int ret = pms_try_fmt_vid_cap(file, fh, fmt);
884
885 if (ret)
886 return ret;
887 mutex_lock(&dev->lock);
888 dev->width = pix->width;
889 dev->height = pix->height;
890 dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
891 pms_resolution(dev, dev->width, dev->height);
892 /* Ok we figured out what to use from our wide choice */
893 mutex_unlock(&dev->lock);
894 return 0;
895}
896
897static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
898{
899 static struct v4l2_fmtdesc formats[] = {
900 { 0, 0, 0,
901 "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
902 { 0, 0, 0, 0 }
903 },
904 { 0, 0, 0,
905 "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
906 { 0, 0, 0, 0 }
907 },
908 };
909 enum v4l2_buf_type type = fmt->type;
910
911 if (fmt->index > 1)
912 return -EINVAL;
913
914 *fmt = formats[fmt->index];
915 fmt->type = type;
916 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917}
918
919static ssize_t pms_read(struct file *file, char __user *buf,
920 size_t count, loff_t *ppos)
921{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300922 struct pms *dev = video_drvdata(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 int len;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300924
Hans Verkuil1ce79812009-11-25 12:37:00 -0300925 mutex_lock(&dev->lock);
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300926 len = pms_capture(dev, buf, (dev->depth == 15), count);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300927 mutex_unlock(&dev->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 return len;
929}
930
Hans Verkuilbec43662008-12-30 06:58:20 -0300931static const struct v4l2_file_operations pms_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 .owner = THIS_MODULE,
Hans Verkuil61df3c92010-11-14 10:09:38 -0300933 .unlocked_ioctl = video_ioctl2,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 .read = pms_read,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935};
936
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300937static const struct v4l2_ioctl_ops pms_ioctl_ops = {
938 .vidioc_querycap = pms_querycap,
939 .vidioc_g_input = pms_g_input,
940 .vidioc_s_input = pms_s_input,
941 .vidioc_enum_input = pms_enum_input,
942 .vidioc_g_std = pms_g_std,
943 .vidioc_s_std = pms_s_std,
944 .vidioc_queryctrl = pms_queryctrl,
945 .vidioc_g_ctrl = pms_g_ctrl,
946 .vidioc_s_ctrl = pms_s_ctrl,
947 .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap,
948 .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap,
949 .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap,
950 .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap,
951};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952
953/*
954 * Probe for and initialise the Mediavision PMS
955 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300956
Hans Verkuil1ce79812009-11-25 12:37:00 -0300957static int init_mediavision(struct pms *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958{
959 int id;
960 int idec, decst;
961 int i;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300962 static const unsigned char i2c_defs[] = {
963 0x4c, 0x30, 0x00, 0xe8,
964 0xb6, 0xe2, 0x00, 0x00,
965 0xff, 0xff, 0x00, 0x00,
966 0x00, 0x00, 0x78, 0x98,
967 0x00, 0x00, 0x00, 0x00,
968 0x34, 0x0a, 0xf4, 0xce,
969 0xe4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 };
971
Hans Verkuil1ce79812009-11-25 12:37:00 -0300972 dev->mem = ioremap(mem_base, 0x800);
973 if (!dev->mem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 return -ENOMEM;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300975
Hans Verkuil1ce79812009-11-25 12:37:00 -0300976 if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
977 printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
978 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 return -EBUSY;
980 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300981 if (!request_region(dev->io, 3, "Mediavision PMS")) {
982 printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
983 release_region(0x9a01, 1);
984 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 return -EBUSY;
986 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300987 outb(0xb8, 0x9a01); /* Unlock */
988 outb(dev->io >> 4, 0x9a01); /* Set IO port */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300989
990
Hans Verkuil1ce79812009-11-25 12:37:00 -0300991 id = mvv_read(dev, 3);
992 decst = pms_i2c_stat(dev, 0x43);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300993
Hans Verkuil1ce79812009-11-25 12:37:00 -0300994 if (decst != -1)
995 idec = 2;
996 else if (pms_i2c_stat(dev, 0xb9) != -1)
997 idec = 3;
998 else if (pms_i2c_stat(dev, 0x8b) != -1)
999 idec = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001000 else
Hans Verkuil1ce79812009-11-25 12:37:00 -03001001 idec = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
1003 printk(KERN_INFO "PMS type is %d\n", idec);
Hans Verkuil1ce79812009-11-25 12:37:00 -03001004 if (idec == 0) {
1005 release_region(dev->io, 3);
1006 release_region(0x9a01, 1);
1007 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 return -ENODEV;
1009 }
1010
1011 /*
1012 * Ok we have a PMS of some sort
1013 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001014
Hans Verkuil1ce79812009-11-25 12:37:00 -03001015 mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001016
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 /* Ok now load the defaults */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001018
Hans Verkuil1ce79812009-11-25 12:37:00 -03001019 for (i = 0; i < 0x19; i++) {
1020 if (i2c_defs[i] == 0xff)
1021 pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 else
Hans Verkuil1ce79812009-11-25 12:37:00 -03001023 pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001025
Hans Verkuil1ce79812009-11-25 12:37:00 -03001026 pms_i2c_write(dev, 0xb8, 0x00, 0x12);
1027 pms_i2c_write(dev, 0xb8, 0x04, 0x00);
1028 pms_i2c_write(dev, 0xb8, 0x07, 0x00);
1029 pms_i2c_write(dev, 0xb8, 0x08, 0x00);
1030 pms_i2c_write(dev, 0xb8, 0x09, 0xff);
1031 pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
1032 pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
1033 pms_i2c_write(dev, 0xb8, 0x10, 0x03);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001034
Hans Verkuil1ce79812009-11-25 12:37:00 -03001035 mvv_write(dev, 0x01, 0x00);
1036 mvv_write(dev, 0x05, 0xa0);
1037 mvv_write(dev, 0x08, 0x25);
1038 mvv_write(dev, 0x09, 0x00);
1039 mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001040
Hans Verkuil1ce79812009-11-25 12:37:00 -03001041 mvv_write(dev, 0x10, 0x02);
1042 mvv_write(dev, 0x1e, 0x0c);
1043 mvv_write(dev, 0x1f, 0x03);
1044 mvv_write(dev, 0x26, 0x06);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001045
Hans Verkuil1ce79812009-11-25 12:37:00 -03001046 mvv_write(dev, 0x2b, 0x00);
1047 mvv_write(dev, 0x2c, 0x20);
1048 mvv_write(dev, 0x2d, 0x00);
1049 mvv_write(dev, 0x2f, 0x70);
1050 mvv_write(dev, 0x32, 0x00);
1051 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
1052 mvv_write(dev, 0x34, 0x00);
1053 mvv_write(dev, 0x35, 0x00);
1054 mvv_write(dev, 0x3a, 0x80);
1055 mvv_write(dev, 0x3b, 0x10);
1056 mvv_write(dev, 0x20, 0x00);
1057 mvv_write(dev, 0x21, 0x00);
1058 mvv_write(dev, 0x30, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 return 0;
1060}
1061
1062/*
1063 * Initialization and module stuff
1064 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001065
Rene Hermanb54ff932008-09-03 16:48:21 -03001066#ifndef MODULE
1067static int enable;
1068module_param(enable, int, 0);
1069#endif
1070
Hans Verkuil1ce79812009-11-25 12:37:00 -03001071static int __init pms_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072{
Hans Verkuil1ce79812009-11-25 12:37:00 -03001073 struct pms *dev = &pms_card;
1074 struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
1075 int res;
1076
1077 strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name));
1078
1079 v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001080
Rene Hermanb54ff932008-09-03 16:48:21 -03001081#ifndef MODULE
1082 if (!enable) {
Hans Verkuil1ce79812009-11-25 12:37:00 -03001083 v4l2_err(v4l2_dev,
1084 "PMS: not enabled, use pms.enable=1 to probe\n");
Rene Hermanb54ff932008-09-03 16:48:21 -03001085 return -ENODEV;
1086 }
1087#endif
1088
Hans Verkuil1ce79812009-11-25 12:37:00 -03001089 dev->decoder = PHILIPS2;
1090 dev->io = io_port;
1091 dev->data = io_port + 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001092
Hans Verkuil1ce79812009-11-25 12:37:00 -03001093 if (init_mediavision(dev)) {
1094 v4l2_err(v4l2_dev, "Board not found.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 return -ENODEV;
1096 }
Hans Verkuil1ce79812009-11-25 12:37:00 -03001097
1098 res = v4l2_device_register(NULL, v4l2_dev);
1099 if (res < 0) {
1100 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
1101 return res;
1102 }
1103
1104 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
1105 dev->vdev.v4l2_dev = v4l2_dev;
1106 dev->vdev.fops = &pms_fops;
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001107 dev->vdev.ioctl_ops = &pms_ioctl_ops;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001108 dev->vdev.release = video_device_release_empty;
1109 video_set_drvdata(&dev->vdev, dev);
1110 mutex_init(&dev->lock);
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001111 dev->std = V4L2_STD_NTSC_M;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001112 dev->height = 240;
1113 dev->width = 320;
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001114 dev->depth = 15;
1115 dev->brightness = 139;
1116 dev->contrast = 70;
1117 dev->hue = 0;
1118 dev->saturation = 64;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001119 pms_swsense(dev, 75);
1120 pms_resolution(dev, 320, 240);
1121 pms_videosource(dev, 0);
1122 pms_vcrinput(dev, 0);
1123 if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
1124 v4l2_device_unregister(&dev->v4l2_dev);
1125 release_region(dev->io, 3);
1126 release_region(0x9a01, 1);
1127 iounmap(dev->mem);
1128 return -EINVAL;
1129 }
1130 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131}
1132
Hans Verkuil1ce79812009-11-25 12:37:00 -03001133static void __exit pms_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134{
Hans Verkuil1ce79812009-11-25 12:37:00 -03001135 struct pms *dev = &pms_card;
1136
1137 video_unregister_device(&dev->vdev);
1138 release_region(dev->io, 3);
1139 release_region(0x9a01, 1);
1140 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141}
1142
Hans Verkuil1ce79812009-11-25 12:37:00 -03001143module_init(pms_init);
1144module_exit(pms_exit);