blob: 0598bbd3f368da323398fedbf378ebedcc21ff3d [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 unsigned long in_use;
65 struct mutex lock;
66 int i2c_count;
67 struct i2c_info i2cinfo[64];
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Hans Verkuil1ce79812009-11-25 12:37:00 -030069 int decoder;
70 int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
Hans Verkuilfeba2f82009-11-25 12:47:02 -030071 v4l2_std_id std;
Hans Verkuil1ce79812009-11-25 12:37:00 -030072 int io;
73 int data;
74 void __iomem *mem;
75};
76
77static struct pms pms_card;
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
79/*
80 * I/O ports and Shared Memory
81 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030082
Hans Verkuil1ce79812009-11-25 12:37:00 -030083static int io_port = 0x250;
84module_param(io_port, int, 0);
85
86static int mem_base = 0xc8000;
87module_param(mem_base, int, 0);
88
89static int video_nr = -1;
90module_param(video_nr, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030092
Hans Verkuil1ce79812009-11-25 12:37:00 -030093static inline void mvv_write(struct pms *dev, u8 index, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -070094{
Hans Verkuil1ce79812009-11-25 12:37:00 -030095 outw(index | (value << 8), dev->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096}
97
Hans Verkuil1ce79812009-11-25 12:37:00 -030098static inline u8 mvv_read(struct pms *dev, u8 index)
Linus Torvalds1da177e2005-04-16 15:20:36 -070099{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300100 outb(index, dev->io);
101 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102}
103
Hans Verkuil1ce79812009-11-25 12:37:00 -0300104static int pms_i2c_stat(struct pms *dev, u8 slave)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300106 int counter = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300108
Hans Verkuil1ce79812009-11-25 12:37:00 -0300109 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300110
Hans Verkuil1ce79812009-11-25 12:37:00 -0300111 while ((inb(dev->data) & 0x01) == 0)
112 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 break;
114
Hans Verkuil1ce79812009-11-25 12:37:00 -0300115 while ((inb(dev->data) & 0x01) != 0)
116 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300118
Hans Verkuil1ce79812009-11-25 12:37:00 -0300119 outb(slave, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300120
Hans Verkuil1ce79812009-11-25 12:37:00 -0300121 counter = 0;
122 while ((inb(dev->data) & 0x01) == 0)
123 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 break;
125
Hans Verkuil1ce79812009-11-25 12:37:00 -0300126 while ((inb(dev->data) & 0x01) != 0)
127 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300129
Hans Verkuil1ce79812009-11-25 12:37:00 -0300130 for (i = 0; i < 12; i++) {
131 char st = inb(dev->data);
132
133 if ((st & 2) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 return -1;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300135 if ((st & 1) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 break;
137 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300138 outb(0x29, dev->io);
139 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140}
141
Hans Verkuil1ce79812009-11-25 12:37:00 -0300142static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300144 int skip = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 int count;
146 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300147
Hans Verkuil1ce79812009-11-25 12:37:00 -0300148 for (i = 0; i < dev->i2c_count; i++) {
149 if ((dev->i2cinfo[i].slave == slave) &&
150 (dev->i2cinfo[i].sub == sub)) {
151 if (dev->i2cinfo[i].data == data)
152 skip = 1;
153 dev->i2cinfo[i].data = data;
154 i = dev->i2c_count + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 }
156 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300157
Hans Verkuil1ce79812009-11-25 12:37:00 -0300158 if (i == dev->i2c_count && dev->i2c_count < 64) {
159 dev->i2cinfo[dev->i2c_count].slave = slave;
160 dev->i2cinfo[dev->i2c_count].sub = sub;
161 dev->i2cinfo[dev->i2c_count].data = data;
162 dev->i2c_count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300164
Hans Verkuil1ce79812009-11-25 12:37:00 -0300165 if (skip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 return 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300167
Hans Verkuil1ce79812009-11-25 12:37:00 -0300168 mvv_write(dev, 0x29, sub);
169 mvv_write(dev, 0x2A, data);
170 mvv_write(dev, 0x28, slave);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300171
Hans Verkuil1ce79812009-11-25 12:37:00 -0300172 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300173
Hans Verkuil1ce79812009-11-25 12:37:00 -0300174 count = 0;
175 while ((inb(dev->data) & 1) == 0)
176 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 break;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300178 while ((inb(dev->data) & 1) != 0)
179 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300181
Hans Verkuil1ce79812009-11-25 12:37:00 -0300182 count = inb(dev->data);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300183
Hans Verkuil1ce79812009-11-25 12:37:00 -0300184 if (count & 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 return -1;
186 return count;
187}
188
Hans Verkuil1ce79812009-11-25 12:37:00 -0300189static int pms_i2c_read(struct pms *dev, int slave, int sub)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300191 int i;
192
193 for (i = 0; i < dev->i2c_count; i++) {
194 if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
195 return dev->i2cinfo[i].data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 }
197 return 0;
198}
199
200
Hans Verkuil1ce79812009-11-25 12:37:00 -0300201static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300203 u8 tmp;
204
Hans Verkuil1ce79812009-11-25 12:37:00 -0300205 tmp = pms_i2c_read(dev, slave, sub);
206 tmp = (tmp & and) | or;
207 pms_i2c_write(dev, slave, sub, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208}
209
210/*
211 * Control functions
212 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300213
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
Hans Verkuil1ce79812009-11-25 12:37:00 -0300215static void pms_videosource(struct pms *dev, short source)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300217 switch (dev->decoder) {
218 case MOTOROLA:
219 break;
220 case PHILIPS2:
221 pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
222 break;
223 case PHILIPS1:
224 break;
225 }
226 mvv_write(dev, 0x2E, 0x31);
227 /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
228 But could not make this work correctly. Only Composite input
229 worked for me. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230}
231
Hans Verkuil1ce79812009-11-25 12:37:00 -0300232static void pms_hue(struct pms *dev, short hue)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300234 switch (dev->decoder) {
235 case MOTOROLA:
236 pms_i2c_write(dev, 0x8a, 0x00, hue);
237 break;
238 case PHILIPS2:
239 pms_i2c_write(dev, 0x8a, 0x07, hue);
240 break;
241 case PHILIPS1:
242 pms_i2c_write(dev, 0x42, 0x07, hue);
243 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 }
245}
246
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300247static void pms_saturation(struct pms *dev, short sat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300249 switch (dev->decoder) {
250 case MOTOROLA:
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300251 pms_i2c_write(dev, 0x8a, 0x00, sat);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300252 break;
253 case PHILIPS1:
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300254 pms_i2c_write(dev, 0x42, 0x12, sat);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300255 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 }
257}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300258
259
Hans Verkuil1ce79812009-11-25 12:37:00 -0300260static void pms_contrast(struct pms *dev, short contrast)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300262 switch (dev->decoder) {
263 case MOTOROLA:
264 pms_i2c_write(dev, 0x8a, 0x00, contrast);
265 break;
266 case PHILIPS1:
267 pms_i2c_write(dev, 0x42, 0x13, contrast);
268 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 }
270}
271
Hans Verkuil1ce79812009-11-25 12:37:00 -0300272static void pms_brightness(struct pms *dev, short brightness)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300274 switch (dev->decoder) {
275 case MOTOROLA:
276 pms_i2c_write(dev, 0x8a, 0x00, brightness);
277 pms_i2c_write(dev, 0x8a, 0x00, brightness);
278 pms_i2c_write(dev, 0x8a, 0x00, brightness);
279 break;
280 case PHILIPS1:
281 pms_i2c_write(dev, 0x42, 0x19, brightness);
282 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 }
284}
285
286
Hans Verkuil1ce79812009-11-25 12:37:00 -0300287static void pms_format(struct pms *dev, short format)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288{
289 int target;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300290
Hans Verkuil1ce79812009-11-25 12:37:00 -0300291 dev->standard = format;
292
293 if (dev->decoder == PHILIPS1)
294 target = 0x42;
295 else if (dev->decoder == PHILIPS2)
296 target = 0x8a;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 else
298 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300299
Hans Verkuil1ce79812009-11-25 12:37:00 -0300300 switch (format) {
301 case 0: /* Auto */
302 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
303 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
304 break;
305 case 1: /* NTSC */
306 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
307 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
308 break;
309 case 2: /* PAL */
310 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
311 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
312 break;
313 case 3: /* SECAM */
314 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
315 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
316 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 }
318}
319
320#ifdef FOR_FUTURE_EXPANSION
321
322/*
323 * These features of the PMS card are not currently exposes. They
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300324 * could become a private v4l ioctl for PMSCONFIG or somesuch if
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 * people need it. We also don't yet use the PMS interrupt.
326 */
327
Hans Verkuil1ce79812009-11-25 12:37:00 -0300328static void pms_hstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300330 switch (dev->decoder) {
331 case PHILIPS1:
332 pms_i2c_write(dev, 0x8a, 0x05, start);
333 pms_i2c_write(dev, 0x8a, 0x18, start);
334 break;
335 case PHILIPS2:
336 pms_i2c_write(dev, 0x42, 0x05, start);
337 pms_i2c_write(dev, 0x42, 0x18, start);
338 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 }
340}
341
342/*
343 * Bandpass filters
344 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300345
Hans Verkuil1ce79812009-11-25 12:37:00 -0300346static void pms_bandpass(struct pms *dev, short pass)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300348 if (dev->decoder == PHILIPS2)
349 pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
350 else if (dev->decoder == PHILIPS1)
351 pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352}
353
Hans Verkuil1ce79812009-11-25 12:37:00 -0300354static void pms_antisnow(struct pms *dev, short snow)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300356 if (dev->decoder == PHILIPS2)
357 pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
358 else if (dev->decoder == PHILIPS1)
359 pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360}
361
Hans Verkuil1ce79812009-11-25 12:37:00 -0300362static void pms_sharpness(struct pms *dev, short sharp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300364 if (dev->decoder == PHILIPS2)
365 pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
366 else if (dev->decoder == PHILIPS1)
367 pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368}
369
Hans Verkuil1ce79812009-11-25 12:37:00 -0300370static void pms_chromaagc(struct pms *dev, short agc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300372 if (dev->decoder == PHILIPS2)
373 pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
374 else if (dev->decoder == PHILIPS1)
375 pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
377
Hans Verkuil1ce79812009-11-25 12:37:00 -0300378static void pms_vertnoise(struct pms *dev, short noise)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300380 if (dev->decoder == PHILIPS2)
381 pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
382 else if (dev->decoder == PHILIPS1)
383 pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384}
385
Hans Verkuil1ce79812009-11-25 12:37:00 -0300386static void pms_forcecolour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300388 if (dev->decoder == PHILIPS2)
389 pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
390 else if (dev->decoder == PHILIPS1)
391 pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392}
393
Hans Verkuil1ce79812009-11-25 12:37:00 -0300394static void pms_antigamma(struct pms *dev, short gamma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300396 if (dev->decoder == PHILIPS2)
397 pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
398 else if (dev->decoder == PHILIPS1)
399 pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400}
401
Hans Verkuil1ce79812009-11-25 12:37:00 -0300402static void pms_prefilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300404 if (dev->decoder == PHILIPS2)
405 pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
406 else if (dev->decoder == PHILIPS1)
407 pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408}
409
Hans Verkuil1ce79812009-11-25 12:37:00 -0300410static void pms_hfilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300412 if (dev->decoder == PHILIPS2)
413 pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
414 else if (dev->decoder == PHILIPS1)
415 pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416}
417
Hans Verkuil1ce79812009-11-25 12:37:00 -0300418static void pms_vfilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300420 if (dev->decoder == PHILIPS2)
421 pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
422 else if (dev->decoder == PHILIPS1)
423 pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424}
425
Hans Verkuil1ce79812009-11-25 12:37:00 -0300426static void pms_killcolour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300428 if (dev->decoder == PHILIPS2) {
429 pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
430 pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
431 } else if (dev->decoder == PHILIPS1) {
432 pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
433 pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 }
435}
436
Hans Verkuil1ce79812009-11-25 12:37:00 -0300437static void pms_chromagain(struct pms *dev, short chroma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300439 if (dev->decoder == PHILIPS2)
440 pms_i2c_write(dev, 0x8a, 0x11, chroma);
441 else if (dev->decoder == PHILIPS1)
442 pms_i2c_write(dev, 0x42, 0x11, chroma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443}
444
445
Hans Verkuil1ce79812009-11-25 12:37:00 -0300446static void pms_spacialcompl(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300448 mvv_write(dev, 0x3b, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449}
450
Hans Verkuil1ce79812009-11-25 12:37:00 -0300451static void pms_spacialcomph(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300453 mvv_write(dev, 0x3a, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454}
455
Hans Verkuil1ce79812009-11-25 12:37:00 -0300456static void pms_vstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300458 mvv_write(dev, 0x16, start);
459 mvv_write(dev, 0x17, (start >> 8) & 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460}
461
462#endif
463
Hans Verkuil1ce79812009-11-25 12:37:00 -0300464static void pms_secamcross(struct pms *dev, short cross)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300466 if (dev->decoder == PHILIPS2)
467 pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
468 else if (dev->decoder == PHILIPS1)
469 pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470}
471
472
Hans Verkuil1ce79812009-11-25 12:37:00 -0300473static void pms_swsense(struct pms *dev, short sense)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300475 if (dev->decoder == PHILIPS2) {
476 pms_i2c_write(dev, 0x8a, 0x0a, sense);
477 pms_i2c_write(dev, 0x8a, 0x0b, sense);
478 } else if (dev->decoder == PHILIPS1) {
479 pms_i2c_write(dev, 0x42, 0x0a, sense);
480 pms_i2c_write(dev, 0x42, 0x0b, sense);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 }
482}
483
484
Hans Verkuil1ce79812009-11-25 12:37:00 -0300485static void pms_framerate(struct pms *dev, short frr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300487 int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300488
489 if (frr == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 return;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300491 fps = fps/frr;
492 mvv_write(dev, 0x14, 0x80 | fps);
493 mvv_write(dev, 0x15, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494}
495
Hans Verkuil1ce79812009-11-25 12:37:00 -0300496static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300498 mvv_write(dev, 0x1c, deciden); /* Denominator */
499 mvv_write(dev, 0x1d, decinum); /* Numerator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500}
501
502/*
503 * Turn 16bit ratios into best small ratio the chipset can grok
504 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300505
Hans Verkuil1ce79812009-11-25 12:37:00 -0300506static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300508 /* Knock it down by / 5 once */
509 if (decinum % 5 == 0) {
510 deciden /= 5;
511 decinum /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 }
513 /*
514 * 3's
515 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300516 while (decinum % 3 == 0 && deciden % 3 == 0) {
517 deciden /= 3;
518 decinum /= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 }
520 /*
521 * 2's
522 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300523 while (decinum % 2 == 0 && deciden % 2 == 0) {
524 decinum /= 2;
525 deciden /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 }
527 /*
528 * Fudgyify
529 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300530 while (deciden > 32) {
531 deciden /= 2;
532 decinum = (decinum + 1) / 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300534 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 deciden--;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300536 pms_vert(dev, deciden, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537}
538
Hans Verkuil1ce79812009-11-25 12:37:00 -0300539static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300541 if (decinum <= 512) {
542 if (decinum % 5 == 0) {
543 decinum /= 5;
544 deciden /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300546 } else {
547 decinum = 512;
548 deciden = 640; /* 768 would be ideal */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300550
Hans Verkuil1ce79812009-11-25 12:37:00 -0300551 while (((decinum | deciden) & 1) == 0) {
552 decinum >>= 1;
553 deciden >>= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300555 while (deciden > 32) {
556 deciden >>= 1;
557 decinum = (decinum + 1) >> 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300559 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 deciden--;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300561
Hans Verkuil1ce79812009-11-25 12:37:00 -0300562 mvv_write(dev, 0x24, 0x80 | deciden);
563 mvv_write(dev, 0x25, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564}
565
Hans Verkuil1ce79812009-11-25 12:37:00 -0300566static void pms_resolution(struct pms *dev, short width, short height)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567{
568 int fg_height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300569
Hans Verkuil1ce79812009-11-25 12:37:00 -0300570 fg_height = height;
571 if (fg_height > 280)
572 fg_height = 280;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300573
Hans Verkuil1ce79812009-11-25 12:37:00 -0300574 mvv_write(dev, 0x18, fg_height);
575 mvv_write(dev, 0x19, fg_height >> 8);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300576
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300577 if (dev->std & V4L2_STD_525_60) {
Hans Verkuil1ce79812009-11-25 12:37:00 -0300578 mvv_write(dev, 0x1a, 0xfc);
579 mvv_write(dev, 0x1b, 0x00);
580 if (height > fg_height)
581 pms_vertdeci(dev, 240, 240);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300583 pms_vertdeci(dev, fg_height, 240);
584 } else {
585 mvv_write(dev, 0x1a, 0x1a);
586 mvv_write(dev, 0x1b, 0x01);
587 if (fg_height > 256)
588 pms_vertdeci(dev, 270, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300590 pms_vertdeci(dev, fg_height, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300592 mvv_write(dev, 0x12, 0);
593 mvv_write(dev, 0x13, MVVMEMORYWIDTH);
594 mvv_write(dev, 0x42, 0x00);
595 mvv_write(dev, 0x43, 0x00);
596 mvv_write(dev, 0x44, MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300597
Hans Verkuil1ce79812009-11-25 12:37:00 -0300598 mvv_write(dev, 0x22, width + 8);
599 mvv_write(dev, 0x23, (width + 8) >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300601 if (dev->std & V4L2_STD_525_60)
Hans Verkuil1ce79812009-11-25 12:37:00 -0300602 pms_horzdeci(dev, width, 640);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300604 pms_horzdeci(dev, width + 8, 768);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
Hans Verkuil1ce79812009-11-25 12:37:00 -0300606 mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
607 mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
608 mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
609 mvv_write(dev, 0x32, 0x00);
610 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611}
612
613
614/*
615 * Set Input
616 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300617
Hans Verkuil1ce79812009-11-25 12:37:00 -0300618static void pms_vcrinput(struct pms *dev, short input)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300620 if (dev->decoder == PHILIPS2)
621 pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
622 else if (dev->decoder == PHILIPS1)
623 pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624}
625
626
Hans Verkuil1ce79812009-11-25 12:37:00 -0300627static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628{
629 int y;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300630 int dw = 2 * dev->width;
631 char tmp[dw + 32]; /* using a temp buffer is faster than direct */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 int cnt = 0;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300633 int len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 unsigned char r8 = 0x5; /* value for reg8 */
635
636 if (rgb555)
637 r8 |= 0x20; /* else use untranslated rgb = 565 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300638 mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300641
Hans Verkuil1ce79812009-11-25 12:37:00 -0300642 for (y = 0; y < dev->height; y++) {
643 writeb(0, dev->mem); /* synchronisiert neue Zeile */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300644
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 /*
646 * This is in truth a fifo, be very careful as if you
647 * forgot this odd things will occur 8)
648 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300649
Hans Verkuil1ce79812009-11-25 12:37:00 -0300650 memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 cnt -= dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300652 while (cnt <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 /*
654 * Don't copy too far
655 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300656 int dt = dw;
657 if (dt + len > count)
658 dt = count - len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 cnt += dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300660 if (copy_to_user(buf, tmp + 32, dt))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 return len ? len : -EFAULT;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300662 buf += dt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 len += dt;
664 }
665 }
666 return len;
667}
668
669
670/*
671 * Video4linux interfacing
672 */
673
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300674static int pms_querycap(struct file *file, void *priv,
675 struct v4l2_capability *vcap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300677 struct pms *dev = video_drvdata(file);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300678
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300679 strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
680 strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
681 strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info));
682 vcap->version = KERNEL_VERSION(0, 0, 3);
683 vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 return 0;
685}
686
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300687static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300689 static const char *inputs[4] = {
690 "Composite",
691 "S-Video",
692 "Composite (VCR)",
693 "S-Video (VCR)"
694 };
695
696 if (vin->index > 3)
697 return -EINVAL;
698 strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
699 vin->type = V4L2_INPUT_TYPE_CAMERA;
700 vin->audioset = 0;
701 vin->tuner = 0;
702 vin->std = V4L2_STD_ALL;
703 vin->status = 0;
704 return 0;
705}
706
707static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
708{
709 struct pms *dev = video_drvdata(file);
710
711 *inp = dev->input;
712 return 0;
713}
714
715static int pms_s_input(struct file *file, void *fh, unsigned int inp)
716{
717 struct pms *dev = video_drvdata(file);
718
719 if (inp > 3)
720 return -EINVAL;
721
722 mutex_lock(&dev->lock);
723 dev->input = inp;
724 pms_videosource(dev, inp & 1);
725 pms_vcrinput(dev, inp >> 1);
726 mutex_unlock(&dev->lock);
727 return 0;
728}
729
730static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
731{
732 struct pms *dev = video_drvdata(file);
733
734 *std = dev->std;
735 return 0;
736}
737
738static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std)
739{
740 struct pms *dev = video_drvdata(file);
741 int ret = 0;
742
743 dev->std = *std;
744 mutex_lock(&dev->lock);
745 if (dev->std & V4L2_STD_NTSC) {
746 pms_framerate(dev, 30);
747 pms_secamcross(dev, 0);
748 pms_format(dev, 1);
749 } else if (dev->std & V4L2_STD_PAL) {
750 pms_framerate(dev, 25);
751 pms_secamcross(dev, 0);
752 pms_format(dev, 2);
753 } else if (dev->std & V4L2_STD_SECAM) {
754 pms_framerate(dev, 25);
755 pms_secamcross(dev, 1);
756 pms_format(dev, 2);
757 } else {
758 ret = -EINVAL;
759 }
760 /*
761 switch (v->mode) {
762 case VIDEO_MODE_AUTO:
763 pms_framerate(dev, 25);
764 pms_secamcross(dev, 0);
765 pms_format(dev, 0);
766 break;
767 }*/
768 mutex_unlock(&dev->lock);
769 return 0;
770}
771
772static int pms_queryctrl(struct file *file, void *priv,
773 struct v4l2_queryctrl *qc)
774{
775 switch (qc->id) {
776 case V4L2_CID_BRIGHTNESS:
777 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139);
778 case V4L2_CID_CONTRAST:
779 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70);
780 case V4L2_CID_SATURATION:
781 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64);
782 case V4L2_CID_HUE:
783 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
784 }
785 return -EINVAL;
786}
787
788static int pms_g_ctrl(struct file *file, void *priv,
789 struct v4l2_control *ctrl)
790{
791 struct pms *dev = video_drvdata(file);
792 int ret = 0;
793
794 switch (ctrl->id) {
795 case V4L2_CID_BRIGHTNESS:
796 ctrl->value = dev->brightness;
797 break;
798 case V4L2_CID_CONTRAST:
799 ctrl->value = dev->contrast;
800 break;
801 case V4L2_CID_SATURATION:
802 ctrl->value = dev->saturation;
803 break;
804 case V4L2_CID_HUE:
805 ctrl->value = dev->hue;
806 break;
807 default:
808 ret = -EINVAL;
809 break;
810 }
811 return ret;
812}
813
814static int pms_s_ctrl(struct file *file, void *priv,
815 struct v4l2_control *ctrl)
816{
817 struct pms *dev = video_drvdata(file);
818 int ret = 0;
819
820 mutex_lock(&dev->lock);
821 switch (ctrl->id) {
822 case V4L2_CID_BRIGHTNESS:
823 dev->brightness = ctrl->value;
824 pms_brightness(dev, dev->brightness);
825 break;
826 case V4L2_CID_CONTRAST:
827 dev->contrast = ctrl->value;
828 pms_contrast(dev, dev->contrast);
829 break;
830 case V4L2_CID_SATURATION:
831 dev->saturation = ctrl->value;
832 pms_saturation(dev, dev->saturation);
833 break;
834 case V4L2_CID_HUE:
835 dev->hue = ctrl->value;
836 pms_hue(dev, dev->hue);
837 break;
838 default:
839 ret = -EINVAL;
840 break;
841 }
842 mutex_unlock(&dev->lock);
843 return ret;
844}
845
846static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
847{
848 struct pms *dev = video_drvdata(file);
849 struct v4l2_pix_format *pix = &fmt->fmt.pix;
850
851 pix->width = dev->width;
852 pix->height = dev->height;
853 pix->pixelformat = dev->width == 15 ?
854 V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
855 pix->field = V4L2_FIELD_NONE;
856 pix->bytesperline = 2 * dev->width;
857 pix->sizeimage = 2 * dev->width * dev->height;
858 /* Just a guess */
859 pix->colorspace = V4L2_COLORSPACE_SRGB;
860 return 0;
861}
862
863static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
864{
865 struct v4l2_pix_format *pix = &fmt->fmt.pix;
866
867 if (pix->height < 16 || pix->height > 480)
868 return -EINVAL;
869 if (pix->width < 16 || pix->width > 640)
870 return -EINVAL;
871 if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
872 pix->pixelformat != V4L2_PIX_FMT_RGB565)
873 return -EINVAL;
874 pix->field = V4L2_FIELD_NONE;
875 pix->bytesperline = 2 * pix->width;
876 pix->sizeimage = 2 * pix->width * pix->height;
877 /* Just a guess */
878 pix->colorspace = V4L2_COLORSPACE_SRGB;
879 return 0;
880}
881
882static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
883{
884 struct pms *dev = video_drvdata(file);
885 struct v4l2_pix_format *pix = &fmt->fmt.pix;
886 int ret = pms_try_fmt_vid_cap(file, fh, fmt);
887
888 if (ret)
889 return ret;
890 mutex_lock(&dev->lock);
891 dev->width = pix->width;
892 dev->height = pix->height;
893 dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
894 pms_resolution(dev, dev->width, dev->height);
895 /* Ok we figured out what to use from our wide choice */
896 mutex_unlock(&dev->lock);
897 return 0;
898}
899
900static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
901{
902 static struct v4l2_fmtdesc formats[] = {
903 { 0, 0, 0,
904 "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
905 { 0, 0, 0, 0 }
906 },
907 { 0, 0, 0,
908 "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
909 { 0, 0, 0, 0 }
910 },
911 };
912 enum v4l2_buf_type type = fmt->type;
913
914 if (fmt->index > 1)
915 return -EINVAL;
916
917 *fmt = formats[fmt->index];
918 fmt->type = type;
919 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920}
921
922static ssize_t pms_read(struct file *file, char __user *buf,
923 size_t count, loff_t *ppos)
924{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300925 struct pms *dev = video_drvdata(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 int len;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300927
Hans Verkuil1ce79812009-11-25 12:37:00 -0300928 mutex_lock(&dev->lock);
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300929 len = pms_capture(dev, buf, (dev->depth == 15), count);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300930 mutex_unlock(&dev->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 return len;
932}
933
Hans Verkuilbec43662008-12-30 06:58:20 -0300934static int pms_exclusive_open(struct file *file)
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300935{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300936 struct pms *dev = video_drvdata(file);
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300937
Hans Verkuil1ce79812009-11-25 12:37:00 -0300938 return test_and_set_bit(0, &dev->in_use) ? -EBUSY : 0;
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300939}
940
Hans Verkuilbec43662008-12-30 06:58:20 -0300941static int pms_exclusive_release(struct file *file)
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300942{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300943 struct pms *dev = video_drvdata(file);
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300944
Hans Verkuil1ce79812009-11-25 12:37:00 -0300945 clear_bit(0, &dev->in_use);
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300946 return 0;
947}
948
Hans Verkuilbec43662008-12-30 06:58:20 -0300949static const struct v4l2_file_operations pms_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 .owner = THIS_MODULE,
Hans Verkuil7d43cd52008-08-23 05:31:47 -0300951 .open = pms_exclusive_open,
952 .release = pms_exclusive_release,
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300953 .ioctl = video_ioctl2,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 .read = pms_read,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955};
956
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300957static const struct v4l2_ioctl_ops pms_ioctl_ops = {
958 .vidioc_querycap = pms_querycap,
959 .vidioc_g_input = pms_g_input,
960 .vidioc_s_input = pms_s_input,
961 .vidioc_enum_input = pms_enum_input,
962 .vidioc_g_std = pms_g_std,
963 .vidioc_s_std = pms_s_std,
964 .vidioc_queryctrl = pms_queryctrl,
965 .vidioc_g_ctrl = pms_g_ctrl,
966 .vidioc_s_ctrl = pms_s_ctrl,
967 .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap,
968 .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap,
969 .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap,
970 .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap,
971};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
973/*
974 * Probe for and initialise the Mediavision PMS
975 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300976
Hans Verkuil1ce79812009-11-25 12:37:00 -0300977static int init_mediavision(struct pms *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978{
979 int id;
980 int idec, decst;
981 int i;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300982 static const unsigned char i2c_defs[] = {
983 0x4c, 0x30, 0x00, 0xe8,
984 0xb6, 0xe2, 0x00, 0x00,
985 0xff, 0xff, 0x00, 0x00,
986 0x00, 0x00, 0x78, 0x98,
987 0x00, 0x00, 0x00, 0x00,
988 0x34, 0x0a, 0xf4, 0xce,
989 0xe4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 };
991
Hans Verkuil1ce79812009-11-25 12:37:00 -0300992 dev->mem = ioremap(mem_base, 0x800);
993 if (!dev->mem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 return -ENOMEM;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300995
Hans Verkuil1ce79812009-11-25 12:37:00 -0300996 if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
997 printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
998 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 return -EBUSY;
1000 }
Hans Verkuil1ce79812009-11-25 12:37:00 -03001001 if (!request_region(dev->io, 3, "Mediavision PMS")) {
1002 printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
1003 release_region(0x9a01, 1);
1004 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 return -EBUSY;
1006 }
Hans Verkuil1ce79812009-11-25 12:37:00 -03001007 outb(0xb8, 0x9a01); /* Unlock */
1008 outb(dev->io >> 4, 0x9a01); /* Set IO port */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001009
1010
Hans Verkuil1ce79812009-11-25 12:37:00 -03001011 id = mvv_read(dev, 3);
1012 decst = pms_i2c_stat(dev, 0x43);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001013
Hans Verkuil1ce79812009-11-25 12:37:00 -03001014 if (decst != -1)
1015 idec = 2;
1016 else if (pms_i2c_stat(dev, 0xb9) != -1)
1017 idec = 3;
1018 else if (pms_i2c_stat(dev, 0x8b) != -1)
1019 idec = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001020 else
Hans Verkuil1ce79812009-11-25 12:37:00 -03001021 idec = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022
1023 printk(KERN_INFO "PMS type is %d\n", idec);
Hans Verkuil1ce79812009-11-25 12:37:00 -03001024 if (idec == 0) {
1025 release_region(dev->io, 3);
1026 release_region(0x9a01, 1);
1027 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 return -ENODEV;
1029 }
1030
1031 /*
1032 * Ok we have a PMS of some sort
1033 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001034
Hans Verkuil1ce79812009-11-25 12:37:00 -03001035 mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001036
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 /* Ok now load the defaults */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001038
Hans Verkuil1ce79812009-11-25 12:37:00 -03001039 for (i = 0; i < 0x19; i++) {
1040 if (i2c_defs[i] == 0xff)
1041 pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 else
Hans Verkuil1ce79812009-11-25 12:37:00 -03001043 pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001045
Hans Verkuil1ce79812009-11-25 12:37:00 -03001046 pms_i2c_write(dev, 0xb8, 0x00, 0x12);
1047 pms_i2c_write(dev, 0xb8, 0x04, 0x00);
1048 pms_i2c_write(dev, 0xb8, 0x07, 0x00);
1049 pms_i2c_write(dev, 0xb8, 0x08, 0x00);
1050 pms_i2c_write(dev, 0xb8, 0x09, 0xff);
1051 pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
1052 pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
1053 pms_i2c_write(dev, 0xb8, 0x10, 0x03);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001054
Hans Verkuil1ce79812009-11-25 12:37:00 -03001055 mvv_write(dev, 0x01, 0x00);
1056 mvv_write(dev, 0x05, 0xa0);
1057 mvv_write(dev, 0x08, 0x25);
1058 mvv_write(dev, 0x09, 0x00);
1059 mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001060
Hans Verkuil1ce79812009-11-25 12:37:00 -03001061 mvv_write(dev, 0x10, 0x02);
1062 mvv_write(dev, 0x1e, 0x0c);
1063 mvv_write(dev, 0x1f, 0x03);
1064 mvv_write(dev, 0x26, 0x06);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001065
Hans Verkuil1ce79812009-11-25 12:37:00 -03001066 mvv_write(dev, 0x2b, 0x00);
1067 mvv_write(dev, 0x2c, 0x20);
1068 mvv_write(dev, 0x2d, 0x00);
1069 mvv_write(dev, 0x2f, 0x70);
1070 mvv_write(dev, 0x32, 0x00);
1071 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
1072 mvv_write(dev, 0x34, 0x00);
1073 mvv_write(dev, 0x35, 0x00);
1074 mvv_write(dev, 0x3a, 0x80);
1075 mvv_write(dev, 0x3b, 0x10);
1076 mvv_write(dev, 0x20, 0x00);
1077 mvv_write(dev, 0x21, 0x00);
1078 mvv_write(dev, 0x30, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 return 0;
1080}
1081
1082/*
1083 * Initialization and module stuff
1084 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001085
Rene Hermanb54ff932008-09-03 16:48:21 -03001086#ifndef MODULE
1087static int enable;
1088module_param(enable, int, 0);
1089#endif
1090
Hans Verkuil1ce79812009-11-25 12:37:00 -03001091static int __init pms_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092{
Hans Verkuil1ce79812009-11-25 12:37:00 -03001093 struct pms *dev = &pms_card;
1094 struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
1095 int res;
1096
1097 strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name));
1098
1099 v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001100
Rene Hermanb54ff932008-09-03 16:48:21 -03001101#ifndef MODULE
1102 if (!enable) {
Hans Verkuil1ce79812009-11-25 12:37:00 -03001103 v4l2_err(v4l2_dev,
1104 "PMS: not enabled, use pms.enable=1 to probe\n");
Rene Hermanb54ff932008-09-03 16:48:21 -03001105 return -ENODEV;
1106 }
1107#endif
1108
Hans Verkuil1ce79812009-11-25 12:37:00 -03001109 dev->decoder = PHILIPS2;
1110 dev->io = io_port;
1111 dev->data = io_port + 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001112
Hans Verkuil1ce79812009-11-25 12:37:00 -03001113 if (init_mediavision(dev)) {
1114 v4l2_err(v4l2_dev, "Board not found.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 return -ENODEV;
1116 }
Hans Verkuil1ce79812009-11-25 12:37:00 -03001117
1118 res = v4l2_device_register(NULL, v4l2_dev);
1119 if (res < 0) {
1120 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
1121 return res;
1122 }
1123
1124 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
1125 dev->vdev.v4l2_dev = v4l2_dev;
1126 dev->vdev.fops = &pms_fops;
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001127 dev->vdev.ioctl_ops = &pms_ioctl_ops;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001128 dev->vdev.release = video_device_release_empty;
1129 video_set_drvdata(&dev->vdev, dev);
1130 mutex_init(&dev->lock);
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001131 dev->std = V4L2_STD_NTSC_M;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001132 dev->height = 240;
1133 dev->width = 320;
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001134 dev->depth = 15;
1135 dev->brightness = 139;
1136 dev->contrast = 70;
1137 dev->hue = 0;
1138 dev->saturation = 64;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001139 pms_swsense(dev, 75);
1140 pms_resolution(dev, 320, 240);
1141 pms_videosource(dev, 0);
1142 pms_vcrinput(dev, 0);
1143 if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
1144 v4l2_device_unregister(&dev->v4l2_dev);
1145 release_region(dev->io, 3);
1146 release_region(0x9a01, 1);
1147 iounmap(dev->mem);
1148 return -EINVAL;
1149 }
1150 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151}
1152
Hans Verkuil1ce79812009-11-25 12:37:00 -03001153static void __exit pms_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154{
Hans Verkuil1ce79812009-11-25 12:37:00 -03001155 struct pms *dev = &pms_card;
1156
1157 video_unregister_device(&dev->vdev);
1158 release_region(dev->io, 3);
1159 release_region(0x9a01, 1);
1160 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161}
1162
Hans Verkuil1ce79812009-11-25 12:37:00 -03001163module_init(pms_init);
1164module_exit(pms_exit);