blob: 7129b50757dbd7605fa13449bd7793511019f52f [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/version.h>
32#include <linux/mutex.h>
Mauro Carvalho Chehab262ab9a2009-12-14 16:43:13 -030033#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <asm/io.h>
Hans Verkuilfeba2f82009-11-25 12:47:02 -030035
36#include <linux/videodev2.h>
Mauro Carvalho Chehab5e87efa2006-06-05 10:26:32 -030037#include <media/v4l2-common.h>
Hans Verkuil35ea11f2008-07-20 08:12:02 -030038#include <media/v4l2-ioctl.h>
Hans Verkuil1ce79812009-11-25 12:37:00 -030039#include <media/v4l2-device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
Hans Verkuil1ce79812009-11-25 12:37:00 -030041MODULE_LICENSE("GPL");
42
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44#define MOTOROLA 1
Hans Verkuilfeba2f82009-11-25 12:47:02 -030045#define PHILIPS2 2 /* SAA7191 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#define PHILIPS1 3
47#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
48
Hans Verkuil1ce79812009-11-25 12:37:00 -030049struct i2c_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 u8 slave;
51 u8 sub;
52 u8 data;
53 u8 hits;
54};
55
Hans Verkuil1ce79812009-11-25 12:37:00 -030056struct pms {
57 struct v4l2_device v4l2_dev;
58 struct video_device vdev;
Hans Verkuil1ce79812009-11-25 12:37:00 -030059 int height;
60 int width;
Hans Verkuilfeba2f82009-11-25 12:47:02 -030061 int depth;
62 int input;
63 s32 brightness, saturation, hue, contrast;
Hans Verkuil1ce79812009-11-25 12:37:00 -030064 struct mutex lock;
65 int i2c_count;
66 struct i2c_info i2cinfo[64];
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
Hans Verkuil1ce79812009-11-25 12:37:00 -030068 int decoder;
69 int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
Hans Verkuilfeba2f82009-11-25 12:47:02 -030070 v4l2_std_id std;
Hans Verkuil1ce79812009-11-25 12:37:00 -030071 int io;
72 int data;
73 void __iomem *mem;
74};
75
76static struct pms pms_card;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78/*
79 * I/O ports and Shared Memory
80 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030081
Hans Verkuil1ce79812009-11-25 12:37:00 -030082static int io_port = 0x250;
83module_param(io_port, int, 0);
84
85static int mem_base = 0xc8000;
86module_param(mem_base, int, 0);
87
88static int video_nr = -1;
89module_param(video_nr, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030091
Hans Verkuil1ce79812009-11-25 12:37:00 -030092static inline void mvv_write(struct pms *dev, u8 index, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093{
Hans Verkuil1ce79812009-11-25 12:37:00 -030094 outw(index | (value << 8), dev->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095}
96
Hans Verkuil1ce79812009-11-25 12:37:00 -030097static inline u8 mvv_read(struct pms *dev, u8 index)
Linus Torvalds1da177e2005-04-16 15:20:36 -070098{
Hans Verkuil1ce79812009-11-25 12:37:00 -030099 outb(index, dev->io);
100 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101}
102
Hans Verkuil1ce79812009-11-25 12:37:00 -0300103static int pms_i2c_stat(struct pms *dev, u8 slave)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300105 int counter = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300107
Hans Verkuil1ce79812009-11-25 12:37:00 -0300108 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300109
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;
113
Hans Verkuil1ce79812009-11-25 12:37:00 -0300114 while ((inb(dev->data) & 0x01) != 0)
115 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300117
Hans Verkuil1ce79812009-11-25 12:37:00 -0300118 outb(slave, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300119
Hans Verkuil1ce79812009-11-25 12:37:00 -0300120 counter = 0;
121 while ((inb(dev->data) & 0x01) == 0)
122 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 break;
124
Hans Verkuil1ce79812009-11-25 12:37:00 -0300125 while ((inb(dev->data) & 0x01) != 0)
126 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300128
Hans Verkuil1ce79812009-11-25 12:37:00 -0300129 for (i = 0; i < 12; i++) {
130 char st = inb(dev->data);
131
132 if ((st & 2) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 return -1;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300134 if ((st & 1) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 break;
136 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300137 outb(0x29, dev->io);
138 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139}
140
Hans Verkuil1ce79812009-11-25 12:37:00 -0300141static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300143 int skip = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 int count;
145 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300146
Hans Verkuil1ce79812009-11-25 12:37:00 -0300147 for (i = 0; i < dev->i2c_count; i++) {
148 if ((dev->i2cinfo[i].slave == slave) &&
149 (dev->i2cinfo[i].sub == sub)) {
150 if (dev->i2cinfo[i].data == data)
151 skip = 1;
152 dev->i2cinfo[i].data = data;
153 i = dev->i2c_count + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 }
155 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300156
Hans Verkuil1ce79812009-11-25 12:37:00 -0300157 if (i == dev->i2c_count && dev->i2c_count < 64) {
158 dev->i2cinfo[dev->i2c_count].slave = slave;
159 dev->i2cinfo[dev->i2c_count].sub = sub;
160 dev->i2cinfo[dev->i2c_count].data = data;
161 dev->i2c_count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300163
Hans Verkuil1ce79812009-11-25 12:37:00 -0300164 if (skip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 return 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300166
Hans Verkuil1ce79812009-11-25 12:37:00 -0300167 mvv_write(dev, 0x29, sub);
168 mvv_write(dev, 0x2A, data);
169 mvv_write(dev, 0x28, slave);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300170
Hans Verkuil1ce79812009-11-25 12:37:00 -0300171 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300172
Hans Verkuil1ce79812009-11-25 12:37:00 -0300173 count = 0;
174 while ((inb(dev->data) & 1) == 0)
175 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 break;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300177 while ((inb(dev->data) & 1) != 0)
178 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300180
Hans Verkuil1ce79812009-11-25 12:37:00 -0300181 count = inb(dev->data);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300182
Hans Verkuil1ce79812009-11-25 12:37:00 -0300183 if (count & 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 return -1;
185 return count;
186}
187
Hans Verkuil1ce79812009-11-25 12:37:00 -0300188static int pms_i2c_read(struct pms *dev, int slave, int sub)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300190 int i;
191
192 for (i = 0; i < dev->i2c_count; i++) {
193 if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
194 return dev->i2cinfo[i].data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 }
196 return 0;
197}
198
199
Hans Verkuil1ce79812009-11-25 12:37:00 -0300200static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300202 u8 tmp;
203
Hans Verkuil1ce79812009-11-25 12:37:00 -0300204 tmp = pms_i2c_read(dev, slave, sub);
205 tmp = (tmp & and) | or;
206 pms_i2c_write(dev, slave, sub, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207}
208
209/*
210 * Control functions
211 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300212
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Hans Verkuil1ce79812009-11-25 12:37:00 -0300214static void pms_videosource(struct pms *dev, short source)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300216 switch (dev->decoder) {
217 case MOTOROLA:
218 break;
219 case PHILIPS2:
220 pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
221 break;
222 case PHILIPS1:
223 break;
224 }
225 mvv_write(dev, 0x2E, 0x31);
226 /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
227 But could not make this work correctly. Only Composite input
228 worked for me. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229}
230
Hans Verkuil1ce79812009-11-25 12:37:00 -0300231static void pms_hue(struct pms *dev, short hue)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300233 switch (dev->decoder) {
234 case MOTOROLA:
235 pms_i2c_write(dev, 0x8a, 0x00, hue);
236 break;
237 case PHILIPS2:
238 pms_i2c_write(dev, 0x8a, 0x07, hue);
239 break;
240 case PHILIPS1:
241 pms_i2c_write(dev, 0x42, 0x07, hue);
242 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 }
244}
245
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300246static void pms_saturation(struct pms *dev, short sat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300248 switch (dev->decoder) {
249 case MOTOROLA:
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300250 pms_i2c_write(dev, 0x8a, 0x00, sat);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300251 break;
252 case PHILIPS1:
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300253 pms_i2c_write(dev, 0x42, 0x12, sat);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300254 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 }
256}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300257
258
Hans Verkuil1ce79812009-11-25 12:37:00 -0300259static void pms_contrast(struct pms *dev, short contrast)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300261 switch (dev->decoder) {
262 case MOTOROLA:
263 pms_i2c_write(dev, 0x8a, 0x00, contrast);
264 break;
265 case PHILIPS1:
266 pms_i2c_write(dev, 0x42, 0x13, contrast);
267 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 }
269}
270
Hans Verkuil1ce79812009-11-25 12:37:00 -0300271static void pms_brightness(struct pms *dev, short brightness)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300273 switch (dev->decoder) {
274 case MOTOROLA:
275 pms_i2c_write(dev, 0x8a, 0x00, brightness);
276 pms_i2c_write(dev, 0x8a, 0x00, brightness);
277 pms_i2c_write(dev, 0x8a, 0x00, brightness);
278 break;
279 case PHILIPS1:
280 pms_i2c_write(dev, 0x42, 0x19, brightness);
281 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 }
283}
284
285
Hans Verkuil1ce79812009-11-25 12:37:00 -0300286static void pms_format(struct pms *dev, short format)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287{
288 int target;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300289
Hans Verkuil1ce79812009-11-25 12:37:00 -0300290 dev->standard = format;
291
292 if (dev->decoder == PHILIPS1)
293 target = 0x42;
294 else if (dev->decoder == PHILIPS2)
295 target = 0x8a;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 else
297 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300298
Hans Verkuil1ce79812009-11-25 12:37:00 -0300299 switch (format) {
300 case 0: /* Auto */
301 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
302 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
303 break;
304 case 1: /* NTSC */
305 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
306 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
307 break;
308 case 2: /* PAL */
309 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
310 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
311 break;
312 case 3: /* SECAM */
313 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
314 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
315 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 }
317}
318
319#ifdef FOR_FUTURE_EXPANSION
320
321/*
322 * These features of the PMS card are not currently exposes. They
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300323 * could become a private v4l ioctl for PMSCONFIG or somesuch if
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 * people need it. We also don't yet use the PMS interrupt.
325 */
326
Hans Verkuil1ce79812009-11-25 12:37:00 -0300327static void pms_hstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300329 switch (dev->decoder) {
330 case PHILIPS1:
331 pms_i2c_write(dev, 0x8a, 0x05, start);
332 pms_i2c_write(dev, 0x8a, 0x18, start);
333 break;
334 case PHILIPS2:
335 pms_i2c_write(dev, 0x42, 0x05, start);
336 pms_i2c_write(dev, 0x42, 0x18, start);
337 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 }
339}
340
341/*
342 * Bandpass filters
343 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300344
Hans Verkuil1ce79812009-11-25 12:37:00 -0300345static void pms_bandpass(struct pms *dev, short pass)
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, 0xcf, (pass & 0x03) << 4);
349 else if (dev->decoder == PHILIPS1)
350 pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351}
352
Hans Verkuil1ce79812009-11-25 12:37:00 -0300353static void pms_antisnow(struct pms *dev, short snow)
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, 0x06, 0xf3, (snow & 0x03) << 2);
357 else if (dev->decoder == PHILIPS1)
358 pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359}
360
Hans Verkuil1ce79812009-11-25 12:37:00 -0300361static void pms_sharpness(struct pms *dev, short sharp)
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, 0x06, 0xfc, sharp & 0x03);
365 else if (dev->decoder == PHILIPS1)
366 pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367}
368
Hans Verkuil1ce79812009-11-25 12:37:00 -0300369static void pms_chromaagc(struct pms *dev, short agc)
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, 0x9f, (agc & 0x03) << 5);
373 else if (dev->decoder == PHILIPS1)
374 pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375}
376
Hans Verkuil1ce79812009-11-25 12:37:00 -0300377static void pms_vertnoise(struct pms *dev, short noise)
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, 0x8a, 0x10, 0xfc, noise & 3);
381 else if (dev->decoder == PHILIPS1)
382 pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383}
384
Hans Verkuil1ce79812009-11-25 12:37:00 -0300385static void pms_forcecolour(struct pms *dev, short colour)
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, 0x0c, 0x7f, (colour & 1) << 7);
389 else if (dev->decoder == PHILIPS1)
390 pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391}
392
Hans Verkuil1ce79812009-11-25 12:37:00 -0300393static void pms_antigamma(struct pms *dev, short gamma)
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, 0x00, 0x7f, (gamma & 1) << 7);
397 else if (dev->decoder == PHILIPS1)
398 pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399}
400
Hans Verkuil1ce79812009-11-25 12:37:00 -0300401static void pms_prefilter(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, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
405 else if (dev->decoder == PHILIPS1)
406 pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
Hans Verkuil1ce79812009-11-25 12:37:00 -0300409static void pms_hfilter(struct pms *dev, short filter)
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, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
413 else if (dev->decoder == PHILIPS1)
414 pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415}
416
Hans Verkuil1ce79812009-11-25 12:37:00 -0300417static void pms_vfilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300419 if (dev->decoder == PHILIPS2)
420 pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
421 else if (dev->decoder == PHILIPS1)
422 pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423}
424
Hans Verkuil1ce79812009-11-25 12:37:00 -0300425static void pms_killcolour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300427 if (dev->decoder == PHILIPS2) {
428 pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
429 pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
430 } else if (dev->decoder == PHILIPS1) {
431 pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
432 pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 }
434}
435
Hans Verkuil1ce79812009-11-25 12:37:00 -0300436static void pms_chromagain(struct pms *dev, short chroma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300438 if (dev->decoder == PHILIPS2)
439 pms_i2c_write(dev, 0x8a, 0x11, chroma);
440 else if (dev->decoder == PHILIPS1)
441 pms_i2c_write(dev, 0x42, 0x11, chroma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442}
443
444
Hans Verkuil1ce79812009-11-25 12:37:00 -0300445static void pms_spacialcompl(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300447 mvv_write(dev, 0x3b, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448}
449
Hans Verkuil1ce79812009-11-25 12:37:00 -0300450static void pms_spacialcomph(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300452 mvv_write(dev, 0x3a, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453}
454
Hans Verkuil1ce79812009-11-25 12:37:00 -0300455static void pms_vstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300457 mvv_write(dev, 0x16, start);
458 mvv_write(dev, 0x17, (start >> 8) & 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459}
460
461#endif
462
Hans Verkuil1ce79812009-11-25 12:37:00 -0300463static void pms_secamcross(struct pms *dev, short cross)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300465 if (dev->decoder == PHILIPS2)
466 pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
467 else if (dev->decoder == PHILIPS1)
468 pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469}
470
471
Hans Verkuil1ce79812009-11-25 12:37:00 -0300472static void pms_swsense(struct pms *dev, short sense)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300474 if (dev->decoder == PHILIPS2) {
475 pms_i2c_write(dev, 0x8a, 0x0a, sense);
476 pms_i2c_write(dev, 0x8a, 0x0b, sense);
477 } else if (dev->decoder == PHILIPS1) {
478 pms_i2c_write(dev, 0x42, 0x0a, sense);
479 pms_i2c_write(dev, 0x42, 0x0b, sense);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 }
481}
482
483
Hans Verkuil1ce79812009-11-25 12:37:00 -0300484static void pms_framerate(struct pms *dev, short frr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300486 int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300487
488 if (frr == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 return;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300490 fps = fps/frr;
491 mvv_write(dev, 0x14, 0x80 | fps);
492 mvv_write(dev, 0x15, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493}
494
Hans Verkuil1ce79812009-11-25 12:37:00 -0300495static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300497 mvv_write(dev, 0x1c, deciden); /* Denominator */
498 mvv_write(dev, 0x1d, decinum); /* Numerator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499}
500
501/*
502 * Turn 16bit ratios into best small ratio the chipset can grok
503 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300504
Hans Verkuil1ce79812009-11-25 12:37:00 -0300505static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300507 /* Knock it down by / 5 once */
508 if (decinum % 5 == 0) {
509 deciden /= 5;
510 decinum /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 }
512 /*
513 * 3's
514 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300515 while (decinum % 3 == 0 && deciden % 3 == 0) {
516 deciden /= 3;
517 decinum /= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 }
519 /*
520 * 2's
521 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300522 while (decinum % 2 == 0 && deciden % 2 == 0) {
523 decinum /= 2;
524 deciden /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 }
526 /*
527 * Fudgyify
528 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300529 while (deciden > 32) {
530 deciden /= 2;
531 decinum = (decinum + 1) / 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300533 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 deciden--;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300535 pms_vert(dev, deciden, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536}
537
Hans Verkuil1ce79812009-11-25 12:37:00 -0300538static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300540 if (decinum <= 512) {
541 if (decinum % 5 == 0) {
542 decinum /= 5;
543 deciden /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300545 } else {
546 decinum = 512;
547 deciden = 640; /* 768 would be ideal */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300549
Hans Verkuil1ce79812009-11-25 12:37:00 -0300550 while (((decinum | deciden) & 1) == 0) {
551 decinum >>= 1;
552 deciden >>= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300554 while (deciden > 32) {
555 deciden >>= 1;
556 decinum = (decinum + 1) >> 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300558 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 deciden--;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300560
Hans Verkuil1ce79812009-11-25 12:37:00 -0300561 mvv_write(dev, 0x24, 0x80 | deciden);
562 mvv_write(dev, 0x25, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563}
564
Hans Verkuil1ce79812009-11-25 12:37:00 -0300565static void pms_resolution(struct pms *dev, short width, short height)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566{
567 int fg_height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300568
Hans Verkuil1ce79812009-11-25 12:37:00 -0300569 fg_height = height;
570 if (fg_height > 280)
571 fg_height = 280;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300572
Hans Verkuil1ce79812009-11-25 12:37:00 -0300573 mvv_write(dev, 0x18, fg_height);
574 mvv_write(dev, 0x19, fg_height >> 8);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300575
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300576 if (dev->std & V4L2_STD_525_60) {
Hans Verkuil1ce79812009-11-25 12:37:00 -0300577 mvv_write(dev, 0x1a, 0xfc);
578 mvv_write(dev, 0x1b, 0x00);
579 if (height > fg_height)
580 pms_vertdeci(dev, 240, 240);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300582 pms_vertdeci(dev, fg_height, 240);
583 } else {
584 mvv_write(dev, 0x1a, 0x1a);
585 mvv_write(dev, 0x1b, 0x01);
586 if (fg_height > 256)
587 pms_vertdeci(dev, 270, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300589 pms_vertdeci(dev, fg_height, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300591 mvv_write(dev, 0x12, 0);
592 mvv_write(dev, 0x13, MVVMEMORYWIDTH);
593 mvv_write(dev, 0x42, 0x00);
594 mvv_write(dev, 0x43, 0x00);
595 mvv_write(dev, 0x44, MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300596
Hans Verkuil1ce79812009-11-25 12:37:00 -0300597 mvv_write(dev, 0x22, width + 8);
598 mvv_write(dev, 0x23, (width + 8) >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300600 if (dev->std & V4L2_STD_525_60)
Hans Verkuil1ce79812009-11-25 12:37:00 -0300601 pms_horzdeci(dev, width, 640);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300603 pms_horzdeci(dev, width + 8, 768);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
Hans Verkuil1ce79812009-11-25 12:37:00 -0300605 mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
606 mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
607 mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
608 mvv_write(dev, 0x32, 0x00);
609 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610}
611
612
613/*
614 * Set Input
615 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300616
Hans Verkuil1ce79812009-11-25 12:37:00 -0300617static void pms_vcrinput(struct pms *dev, short input)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300619 if (dev->decoder == PHILIPS2)
620 pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
621 else if (dev->decoder == PHILIPS1)
622 pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623}
624
625
Hans Verkuil1ce79812009-11-25 12:37:00 -0300626static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627{
628 int y;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300629 int dw = 2 * dev->width;
630 char tmp[dw + 32]; /* using a temp buffer is faster than direct */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 int cnt = 0;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300632 int len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 unsigned char r8 = 0x5; /* value for reg8 */
634
635 if (rgb555)
636 r8 |= 0x20; /* else use untranslated rgb = 565 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300637 mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
639/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300640
Hans Verkuil1ce79812009-11-25 12:37:00 -0300641 for (y = 0; y < dev->height; y++) {
642 writeb(0, dev->mem); /* synchronisiert neue Zeile */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300643
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 /*
645 * This is in truth a fifo, be very careful as if you
646 * forgot this odd things will occur 8)
647 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300648
Hans Verkuil1ce79812009-11-25 12:37:00 -0300649 memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 cnt -= dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300651 while (cnt <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 /*
653 * Don't copy too far
654 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300655 int dt = dw;
656 if (dt + len > count)
657 dt = count - len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 cnt += dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300659 if (copy_to_user(buf, tmp + 32, dt))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 return len ? len : -EFAULT;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300661 buf += dt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 len += dt;
663 }
664 }
665 return len;
666}
667
668
669/*
670 * Video4linux interfacing
671 */
672
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300673static int pms_querycap(struct file *file, void *priv,
674 struct v4l2_capability *vcap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300676 struct pms *dev = video_drvdata(file);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300677
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300678 strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
679 strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
680 strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info));
681 vcap->version = KERNEL_VERSION(0, 0, 3);
682 vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return 0;
684}
685
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300686static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300688 static const char *inputs[4] = {
689 "Composite",
690 "S-Video",
691 "Composite (VCR)",
692 "S-Video (VCR)"
693 };
694
695 if (vin->index > 3)
696 return -EINVAL;
697 strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
698 vin->type = V4L2_INPUT_TYPE_CAMERA;
699 vin->audioset = 0;
700 vin->tuner = 0;
701 vin->std = V4L2_STD_ALL;
702 vin->status = 0;
703 return 0;
704}
705
706static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
707{
708 struct pms *dev = video_drvdata(file);
709
710 *inp = dev->input;
711 return 0;
712}
713
714static int pms_s_input(struct file *file, void *fh, unsigned int inp)
715{
716 struct pms *dev = video_drvdata(file);
717
718 if (inp > 3)
719 return -EINVAL;
720
721 mutex_lock(&dev->lock);
722 dev->input = inp;
723 pms_videosource(dev, inp & 1);
724 pms_vcrinput(dev, inp >> 1);
725 mutex_unlock(&dev->lock);
726 return 0;
727}
728
729static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
730{
731 struct pms *dev = video_drvdata(file);
732
733 *std = dev->std;
734 return 0;
735}
736
737static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std)
738{
739 struct pms *dev = video_drvdata(file);
740 int ret = 0;
741
742 dev->std = *std;
743 mutex_lock(&dev->lock);
744 if (dev->std & V4L2_STD_NTSC) {
745 pms_framerate(dev, 30);
746 pms_secamcross(dev, 0);
747 pms_format(dev, 1);
748 } else if (dev->std & V4L2_STD_PAL) {
749 pms_framerate(dev, 25);
750 pms_secamcross(dev, 0);
751 pms_format(dev, 2);
752 } else if (dev->std & V4L2_STD_SECAM) {
753 pms_framerate(dev, 25);
754 pms_secamcross(dev, 1);
755 pms_format(dev, 2);
756 } else {
757 ret = -EINVAL;
758 }
759 /*
760 switch (v->mode) {
761 case VIDEO_MODE_AUTO:
762 pms_framerate(dev, 25);
763 pms_secamcross(dev, 0);
764 pms_format(dev, 0);
765 break;
766 }*/
767 mutex_unlock(&dev->lock);
768 return 0;
769}
770
771static int pms_queryctrl(struct file *file, void *priv,
772 struct v4l2_queryctrl *qc)
773{
774 switch (qc->id) {
775 case V4L2_CID_BRIGHTNESS:
776 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139);
777 case V4L2_CID_CONTRAST:
778 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70);
779 case V4L2_CID_SATURATION:
780 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64);
781 case V4L2_CID_HUE:
782 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
783 }
784 return -EINVAL;
785}
786
787static int pms_g_ctrl(struct file *file, void *priv,
788 struct v4l2_control *ctrl)
789{
790 struct pms *dev = video_drvdata(file);
791 int ret = 0;
792
793 switch (ctrl->id) {
794 case V4L2_CID_BRIGHTNESS:
795 ctrl->value = dev->brightness;
796 break;
797 case V4L2_CID_CONTRAST:
798 ctrl->value = dev->contrast;
799 break;
800 case V4L2_CID_SATURATION:
801 ctrl->value = dev->saturation;
802 break;
803 case V4L2_CID_HUE:
804 ctrl->value = dev->hue;
805 break;
806 default:
807 ret = -EINVAL;
808 break;
809 }
810 return ret;
811}
812
813static int pms_s_ctrl(struct file *file, void *priv,
814 struct v4l2_control *ctrl)
815{
816 struct pms *dev = video_drvdata(file);
817 int ret = 0;
818
819 mutex_lock(&dev->lock);
820 switch (ctrl->id) {
821 case V4L2_CID_BRIGHTNESS:
822 dev->brightness = ctrl->value;
823 pms_brightness(dev, dev->brightness);
824 break;
825 case V4L2_CID_CONTRAST:
826 dev->contrast = ctrl->value;
827 pms_contrast(dev, dev->contrast);
828 break;
829 case V4L2_CID_SATURATION:
830 dev->saturation = ctrl->value;
831 pms_saturation(dev, dev->saturation);
832 break;
833 case V4L2_CID_HUE:
834 dev->hue = ctrl->value;
835 pms_hue(dev, dev->hue);
836 break;
837 default:
838 ret = -EINVAL;
839 break;
840 }
841 mutex_unlock(&dev->lock);
842 return ret;
843}
844
845static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
846{
847 struct pms *dev = video_drvdata(file);
848 struct v4l2_pix_format *pix = &fmt->fmt.pix;
849
850 pix->width = dev->width;
851 pix->height = dev->height;
852 pix->pixelformat = dev->width == 15 ?
853 V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
854 pix->field = V4L2_FIELD_NONE;
855 pix->bytesperline = 2 * dev->width;
856 pix->sizeimage = 2 * dev->width * dev->height;
857 /* Just a guess */
858 pix->colorspace = V4L2_COLORSPACE_SRGB;
859 return 0;
860}
861
862static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
863{
864 struct v4l2_pix_format *pix = &fmt->fmt.pix;
865
866 if (pix->height < 16 || pix->height > 480)
867 return -EINVAL;
868 if (pix->width < 16 || pix->width > 640)
869 return -EINVAL;
870 if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
871 pix->pixelformat != V4L2_PIX_FMT_RGB565)
872 return -EINVAL;
873 pix->field = V4L2_FIELD_NONE;
874 pix->bytesperline = 2 * pix->width;
875 pix->sizeimage = 2 * pix->width * pix->height;
876 /* Just a guess */
877 pix->colorspace = V4L2_COLORSPACE_SRGB;
878 return 0;
879}
880
881static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
882{
883 struct pms *dev = video_drvdata(file);
884 struct v4l2_pix_format *pix = &fmt->fmt.pix;
885 int ret = pms_try_fmt_vid_cap(file, fh, fmt);
886
887 if (ret)
888 return ret;
889 mutex_lock(&dev->lock);
890 dev->width = pix->width;
891 dev->height = pix->height;
892 dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
893 pms_resolution(dev, dev->width, dev->height);
894 /* Ok we figured out what to use from our wide choice */
895 mutex_unlock(&dev->lock);
896 return 0;
897}
898
899static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
900{
901 static struct v4l2_fmtdesc formats[] = {
902 { 0, 0, 0,
903 "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
904 { 0, 0, 0, 0 }
905 },
906 { 0, 0, 0,
907 "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
908 { 0, 0, 0, 0 }
909 },
910 };
911 enum v4l2_buf_type type = fmt->type;
912
913 if (fmt->index > 1)
914 return -EINVAL;
915
916 *fmt = formats[fmt->index];
917 fmt->type = type;
918 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919}
920
921static ssize_t pms_read(struct file *file, char __user *buf,
922 size_t count, loff_t *ppos)
923{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300924 struct pms *dev = video_drvdata(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 int len;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300926
Hans Verkuil1ce79812009-11-25 12:37:00 -0300927 mutex_lock(&dev->lock);
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300928 len = pms_capture(dev, buf, (dev->depth == 15), count);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300929 mutex_unlock(&dev->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 return len;
931}
932
Hans Verkuilbec43662008-12-30 06:58:20 -0300933static const struct v4l2_file_operations pms_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 .owner = THIS_MODULE,
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300935 .ioctl = video_ioctl2,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 .read = pms_read,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937};
938
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300939static const struct v4l2_ioctl_ops pms_ioctl_ops = {
940 .vidioc_querycap = pms_querycap,
941 .vidioc_g_input = pms_g_input,
942 .vidioc_s_input = pms_s_input,
943 .vidioc_enum_input = pms_enum_input,
944 .vidioc_g_std = pms_g_std,
945 .vidioc_s_std = pms_s_std,
946 .vidioc_queryctrl = pms_queryctrl,
947 .vidioc_g_ctrl = pms_g_ctrl,
948 .vidioc_s_ctrl = pms_s_ctrl,
949 .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap,
950 .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap,
951 .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap,
952 .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap,
953};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
955/*
956 * Probe for and initialise the Mediavision PMS
957 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300958
Hans Verkuil1ce79812009-11-25 12:37:00 -0300959static int init_mediavision(struct pms *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960{
961 int id;
962 int idec, decst;
963 int i;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300964 static const unsigned char i2c_defs[] = {
965 0x4c, 0x30, 0x00, 0xe8,
966 0xb6, 0xe2, 0x00, 0x00,
967 0xff, 0xff, 0x00, 0x00,
968 0x00, 0x00, 0x78, 0x98,
969 0x00, 0x00, 0x00, 0x00,
970 0x34, 0x0a, 0xf4, 0xce,
971 0xe4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 };
973
Hans Verkuil1ce79812009-11-25 12:37:00 -0300974 dev->mem = ioremap(mem_base, 0x800);
975 if (!dev->mem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 return -ENOMEM;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300977
Hans Verkuil1ce79812009-11-25 12:37:00 -0300978 if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
979 printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
980 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 return -EBUSY;
982 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300983 if (!request_region(dev->io, 3, "Mediavision PMS")) {
984 printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
985 release_region(0x9a01, 1);
986 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 return -EBUSY;
988 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300989 outb(0xb8, 0x9a01); /* Unlock */
990 outb(dev->io >> 4, 0x9a01); /* Set IO port */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300991
992
Hans Verkuil1ce79812009-11-25 12:37:00 -0300993 id = mvv_read(dev, 3);
994 decst = pms_i2c_stat(dev, 0x43);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300995
Hans Verkuil1ce79812009-11-25 12:37:00 -0300996 if (decst != -1)
997 idec = 2;
998 else if (pms_i2c_stat(dev, 0xb9) != -1)
999 idec = 3;
1000 else if (pms_i2c_stat(dev, 0x8b) != -1)
1001 idec = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001002 else
Hans Verkuil1ce79812009-11-25 12:37:00 -03001003 idec = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
1005 printk(KERN_INFO "PMS type is %d\n", idec);
Hans Verkuil1ce79812009-11-25 12:37:00 -03001006 if (idec == 0) {
1007 release_region(dev->io, 3);
1008 release_region(0x9a01, 1);
1009 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 return -ENODEV;
1011 }
1012
1013 /*
1014 * Ok we have a PMS of some sort
1015 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001016
Hans Verkuil1ce79812009-11-25 12:37:00 -03001017 mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001018
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 /* Ok now load the defaults */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001020
Hans Verkuil1ce79812009-11-25 12:37:00 -03001021 for (i = 0; i < 0x19; i++) {
1022 if (i2c_defs[i] == 0xff)
1023 pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 else
Hans Verkuil1ce79812009-11-25 12:37:00 -03001025 pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001027
Hans Verkuil1ce79812009-11-25 12:37:00 -03001028 pms_i2c_write(dev, 0xb8, 0x00, 0x12);
1029 pms_i2c_write(dev, 0xb8, 0x04, 0x00);
1030 pms_i2c_write(dev, 0xb8, 0x07, 0x00);
1031 pms_i2c_write(dev, 0xb8, 0x08, 0x00);
1032 pms_i2c_write(dev, 0xb8, 0x09, 0xff);
1033 pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
1034 pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
1035 pms_i2c_write(dev, 0xb8, 0x10, 0x03);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001036
Hans Verkuil1ce79812009-11-25 12:37:00 -03001037 mvv_write(dev, 0x01, 0x00);
1038 mvv_write(dev, 0x05, 0xa0);
1039 mvv_write(dev, 0x08, 0x25);
1040 mvv_write(dev, 0x09, 0x00);
1041 mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001042
Hans Verkuil1ce79812009-11-25 12:37:00 -03001043 mvv_write(dev, 0x10, 0x02);
1044 mvv_write(dev, 0x1e, 0x0c);
1045 mvv_write(dev, 0x1f, 0x03);
1046 mvv_write(dev, 0x26, 0x06);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001047
Hans Verkuil1ce79812009-11-25 12:37:00 -03001048 mvv_write(dev, 0x2b, 0x00);
1049 mvv_write(dev, 0x2c, 0x20);
1050 mvv_write(dev, 0x2d, 0x00);
1051 mvv_write(dev, 0x2f, 0x70);
1052 mvv_write(dev, 0x32, 0x00);
1053 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
1054 mvv_write(dev, 0x34, 0x00);
1055 mvv_write(dev, 0x35, 0x00);
1056 mvv_write(dev, 0x3a, 0x80);
1057 mvv_write(dev, 0x3b, 0x10);
1058 mvv_write(dev, 0x20, 0x00);
1059 mvv_write(dev, 0x21, 0x00);
1060 mvv_write(dev, 0x30, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 return 0;
1062}
1063
1064/*
1065 * Initialization and module stuff
1066 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001067
Rene Hermanb54ff932008-09-03 16:48:21 -03001068#ifndef MODULE
1069static int enable;
1070module_param(enable, int, 0);
1071#endif
1072
Hans Verkuil1ce79812009-11-25 12:37:00 -03001073static int __init pms_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074{
Hans Verkuil1ce79812009-11-25 12:37:00 -03001075 struct pms *dev = &pms_card;
1076 struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
1077 int res;
1078
1079 strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name));
1080
1081 v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001082
Rene Hermanb54ff932008-09-03 16:48:21 -03001083#ifndef MODULE
1084 if (!enable) {
Hans Verkuil1ce79812009-11-25 12:37:00 -03001085 v4l2_err(v4l2_dev,
1086 "PMS: not enabled, use pms.enable=1 to probe\n");
Rene Hermanb54ff932008-09-03 16:48:21 -03001087 return -ENODEV;
1088 }
1089#endif
1090
Hans Verkuil1ce79812009-11-25 12:37:00 -03001091 dev->decoder = PHILIPS2;
1092 dev->io = io_port;
1093 dev->data = io_port + 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001094
Hans Verkuil1ce79812009-11-25 12:37:00 -03001095 if (init_mediavision(dev)) {
1096 v4l2_err(v4l2_dev, "Board not found.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 return -ENODEV;
1098 }
Hans Verkuil1ce79812009-11-25 12:37:00 -03001099
1100 res = v4l2_device_register(NULL, v4l2_dev);
1101 if (res < 0) {
1102 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
1103 return res;
1104 }
1105
1106 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
1107 dev->vdev.v4l2_dev = v4l2_dev;
1108 dev->vdev.fops = &pms_fops;
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001109 dev->vdev.ioctl_ops = &pms_ioctl_ops;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001110 dev->vdev.release = video_device_release_empty;
1111 video_set_drvdata(&dev->vdev, dev);
1112 mutex_init(&dev->lock);
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001113 dev->std = V4L2_STD_NTSC_M;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001114 dev->height = 240;
1115 dev->width = 320;
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001116 dev->depth = 15;
1117 dev->brightness = 139;
1118 dev->contrast = 70;
1119 dev->hue = 0;
1120 dev->saturation = 64;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001121 pms_swsense(dev, 75);
1122 pms_resolution(dev, 320, 240);
1123 pms_videosource(dev, 0);
1124 pms_vcrinput(dev, 0);
1125 if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
1126 v4l2_device_unregister(&dev->v4l2_dev);
1127 release_region(dev->io, 3);
1128 release_region(0x9a01, 1);
1129 iounmap(dev->mem);
1130 return -EINVAL;
1131 }
1132 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133}
1134
Hans Verkuil1ce79812009-11-25 12:37:00 -03001135static void __exit pms_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136{
Hans Verkuil1ce79812009-11-25 12:37:00 -03001137 struct pms *dev = &pms_card;
1138
1139 video_unregister_device(&dev->vdev);
1140 release_region(dev->io, 3);
1141 release_region(0x9a01, 1);
1142 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143}
1144
Hans Verkuil1ce79812009-11-25 12:37:00 -03001145module_init(pms_init);
1146module_exit(pms_exit);