blob: e6b497528ceaca5dcc671ad7a92d080936a2a316 [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>
Fengguang Wu4d146ad2012-06-09 20:00:19 -030029#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/ioport.h>
31#include <linux/init.h>
Hans Verkuilfeba2f82009-11-25 12:47:02 -030032#include <linux/mutex.h>
Mauro Carvalho Chehab262ab9a2009-12-14 16:43:13 -030033#include <linux/uaccess.h>
Hans Verkuil4b255242012-05-06 10:41:54 -030034#include <linux/isa.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <asm/io.h>
Hans Verkuilfeba2f82009-11-25 12:47:02 -030036
37#include <linux/videodev2.h>
Mauro Carvalho Chehab5e87efa2006-06-05 10:26:32 -030038#include <media/v4l2-common.h>
Hans Verkuil35ea11f2008-07-20 08:12:02 -030039#include <media/v4l2-ioctl.h>
Hans Verkuil4b255242012-05-06 10:41:54 -030040#include <media/v4l2-ctrls.h>
41#include <media/v4l2-fh.h>
42#include <media/v4l2-event.h>
Hans Verkuil1ce79812009-11-25 12:37:00 -030043#include <media/v4l2-device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Hans Verkuil1ce79812009-11-25 12:37:00 -030045MODULE_LICENSE("GPL");
Hans Verkuil4b255242012-05-06 10:41:54 -030046MODULE_VERSION("0.0.5");
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
48#define MOTOROLA 1
Hans Verkuilfeba2f82009-11-25 12:47:02 -030049#define PHILIPS2 2 /* SAA7191 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#define PHILIPS1 3
51#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
52
Hans Verkuil1ce79812009-11-25 12:37:00 -030053struct i2c_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 u8 slave;
55 u8 sub;
56 u8 data;
57 u8 hits;
58};
59
Hans Verkuil1ce79812009-11-25 12:37:00 -030060struct pms {
61 struct v4l2_device v4l2_dev;
62 struct video_device vdev;
Hans Verkuil4b255242012-05-06 10:41:54 -030063 struct v4l2_ctrl_handler hdl;
Hans Verkuil1ce79812009-11-25 12:37:00 -030064 int height;
65 int width;
Hans Verkuilfeba2f82009-11-25 12:47:02 -030066 int depth;
67 int input;
Hans Verkuil1ce79812009-11-25 12:37:00 -030068 struct mutex lock;
69 int i2c_count;
70 struct i2c_info i2cinfo[64];
Linus Torvalds1da177e2005-04-16 15:20:36 -070071
Hans Verkuil1ce79812009-11-25 12:37:00 -030072 int decoder;
73 int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
Hans Verkuilfeba2f82009-11-25 12:47:02 -030074 v4l2_std_id std;
Hans Verkuil1ce79812009-11-25 12:37:00 -030075 int io;
76 int data;
77 void __iomem *mem;
78};
79
Linus Torvalds1da177e2005-04-16 15:20:36 -070080/*
81 * I/O ports and Shared Memory
82 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030083
Hans Verkuil1ce79812009-11-25 12:37:00 -030084static int io_port = 0x250;
85module_param(io_port, int, 0);
86
87static int mem_base = 0xc8000;
88module_param(mem_base, int, 0);
89
90static int video_nr = -1;
91module_param(video_nr, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030093
Hans Verkuil1ce79812009-11-25 12:37:00 -030094static inline void mvv_write(struct pms *dev, u8 index, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
Hans Verkuil1ce79812009-11-25 12:37:00 -030096 outw(index | (value << 8), dev->io);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097}
98
Hans Verkuil1ce79812009-11-25 12:37:00 -030099static inline u8 mvv_read(struct pms *dev, u8 index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300101 outb(index, dev->io);
102 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103}
104
Hans Verkuil1ce79812009-11-25 12:37:00 -0300105static int pms_i2c_stat(struct pms *dev, u8 slave)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300107 int counter = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300109
Hans Verkuil1ce79812009-11-25 12:37:00 -0300110 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300111
Hans Verkuil1ce79812009-11-25 12:37:00 -0300112 while ((inb(dev->data) & 0x01) == 0)
113 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 break;
115
Hans Verkuil1ce79812009-11-25 12:37:00 -0300116 while ((inb(dev->data) & 0x01) != 0)
117 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300119
Hans Verkuil1ce79812009-11-25 12:37:00 -0300120 outb(slave, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300121
Hans Verkuil1ce79812009-11-25 12:37:00 -0300122 counter = 0;
123 while ((inb(dev->data) & 0x01) == 0)
124 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 break;
126
Hans Verkuil1ce79812009-11-25 12:37:00 -0300127 while ((inb(dev->data) & 0x01) != 0)
128 if (counter++ == 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300130
Hans Verkuil1ce79812009-11-25 12:37:00 -0300131 for (i = 0; i < 12; i++) {
132 char st = inb(dev->data);
133
134 if ((st & 2) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 return -1;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300136 if ((st & 1) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 break;
138 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300139 outb(0x29, dev->io);
140 return inb(dev->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141}
142
Hans Verkuil1ce79812009-11-25 12:37:00 -0300143static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300145 int skip = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 int count;
147 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300148
Hans Verkuil1ce79812009-11-25 12:37:00 -0300149 for (i = 0; i < dev->i2c_count; i++) {
150 if ((dev->i2cinfo[i].slave == slave) &&
151 (dev->i2cinfo[i].sub == sub)) {
152 if (dev->i2cinfo[i].data == data)
153 skip = 1;
154 dev->i2cinfo[i].data = data;
155 i = dev->i2c_count + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 }
157 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300158
Hans Verkuil1ce79812009-11-25 12:37:00 -0300159 if (i == dev->i2c_count && dev->i2c_count < 64) {
160 dev->i2cinfo[dev->i2c_count].slave = slave;
161 dev->i2cinfo[dev->i2c_count].sub = sub;
162 dev->i2cinfo[dev->i2c_count].data = data;
163 dev->i2c_count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300165
Hans Verkuil1ce79812009-11-25 12:37:00 -0300166 if (skip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 return 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300168
Hans Verkuil1ce79812009-11-25 12:37:00 -0300169 mvv_write(dev, 0x29, sub);
170 mvv_write(dev, 0x2A, data);
171 mvv_write(dev, 0x28, slave);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300172
Hans Verkuil1ce79812009-11-25 12:37:00 -0300173 outb(0x28, dev->io);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300174
Hans Verkuil1ce79812009-11-25 12:37:00 -0300175 count = 0;
176 while ((inb(dev->data) & 1) == 0)
177 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 break;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300179 while ((inb(dev->data) & 1) != 0)
180 if (count > 255)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300182
Hans Verkuil1ce79812009-11-25 12:37:00 -0300183 count = inb(dev->data);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300184
Hans Verkuil1ce79812009-11-25 12:37:00 -0300185 if (count & 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 return -1;
187 return count;
188}
189
Hans Verkuil1ce79812009-11-25 12:37:00 -0300190static int pms_i2c_read(struct pms *dev, int slave, int sub)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300192 int i;
193
194 for (i = 0; i < dev->i2c_count; i++) {
195 if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
196 return dev->i2cinfo[i].data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 }
198 return 0;
199}
200
201
Hans Verkuil1ce79812009-11-25 12:37:00 -0300202static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300204 u8 tmp;
205
Hans Verkuil1ce79812009-11-25 12:37:00 -0300206 tmp = pms_i2c_read(dev, slave, sub);
207 tmp = (tmp & and) | or;
208 pms_i2c_write(dev, slave, sub, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209}
210
211/*
212 * Control functions
213 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300214
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
Hans Verkuil1ce79812009-11-25 12:37:00 -0300216static void pms_videosource(struct pms *dev, short source)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300218 switch (dev->decoder) {
219 case MOTOROLA:
220 break;
221 case PHILIPS2:
222 pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
223 break;
224 case PHILIPS1:
225 break;
226 }
227 mvv_write(dev, 0x2E, 0x31);
228 /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
229 But could not make this work correctly. Only Composite input
230 worked for me. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231}
232
Hans Verkuil1ce79812009-11-25 12:37:00 -0300233static void pms_hue(struct pms *dev, short hue)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300235 switch (dev->decoder) {
236 case MOTOROLA:
237 pms_i2c_write(dev, 0x8a, 0x00, hue);
238 break;
239 case PHILIPS2:
240 pms_i2c_write(dev, 0x8a, 0x07, hue);
241 break;
242 case PHILIPS1:
243 pms_i2c_write(dev, 0x42, 0x07, hue);
244 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 }
246}
247
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300248static void pms_saturation(struct pms *dev, short sat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300250 switch (dev->decoder) {
251 case MOTOROLA:
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300252 pms_i2c_write(dev, 0x8a, 0x00, sat);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300253 break;
254 case PHILIPS1:
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300255 pms_i2c_write(dev, 0x42, 0x12, sat);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300256 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 }
258}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300259
260
Hans Verkuil1ce79812009-11-25 12:37:00 -0300261static void pms_contrast(struct pms *dev, short contrast)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300263 switch (dev->decoder) {
264 case MOTOROLA:
265 pms_i2c_write(dev, 0x8a, 0x00, contrast);
266 break;
267 case PHILIPS1:
268 pms_i2c_write(dev, 0x42, 0x13, contrast);
269 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 }
271}
272
Hans Verkuil1ce79812009-11-25 12:37:00 -0300273static void pms_brightness(struct pms *dev, short brightness)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300275 switch (dev->decoder) {
276 case MOTOROLA:
277 pms_i2c_write(dev, 0x8a, 0x00, brightness);
278 pms_i2c_write(dev, 0x8a, 0x00, brightness);
279 pms_i2c_write(dev, 0x8a, 0x00, brightness);
280 break;
281 case PHILIPS1:
282 pms_i2c_write(dev, 0x42, 0x19, brightness);
283 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 }
285}
286
287
Hans Verkuil1ce79812009-11-25 12:37:00 -0300288static void pms_format(struct pms *dev, short format)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289{
290 int target;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300291
Hans Verkuil1ce79812009-11-25 12:37:00 -0300292 dev->standard = format;
293
294 if (dev->decoder == PHILIPS1)
295 target = 0x42;
296 else if (dev->decoder == PHILIPS2)
297 target = 0x8a;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 else
299 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300300
Hans Verkuil1ce79812009-11-25 12:37:00 -0300301 switch (format) {
302 case 0: /* Auto */
303 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
304 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
305 break;
306 case 1: /* NTSC */
307 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
308 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
309 break;
310 case 2: /* PAL */
311 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
312 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
313 break;
314 case 3: /* SECAM */
315 pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
316 pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
317 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 }
319}
320
321#ifdef FOR_FUTURE_EXPANSION
322
323/*
324 * These features of the PMS card are not currently exposes. They
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300325 * could become a private v4l ioctl for PMSCONFIG or somesuch if
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 * people need it. We also don't yet use the PMS interrupt.
327 */
328
Hans Verkuil1ce79812009-11-25 12:37:00 -0300329static void pms_hstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300331 switch (dev->decoder) {
332 case PHILIPS1:
333 pms_i2c_write(dev, 0x8a, 0x05, start);
334 pms_i2c_write(dev, 0x8a, 0x18, start);
335 break;
336 case PHILIPS2:
337 pms_i2c_write(dev, 0x42, 0x05, start);
338 pms_i2c_write(dev, 0x42, 0x18, start);
339 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 }
341}
342
343/*
344 * Bandpass filters
345 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300346
Hans Verkuil1ce79812009-11-25 12:37:00 -0300347static void pms_bandpass(struct pms *dev, short pass)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300349 if (dev->decoder == PHILIPS2)
350 pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
351 else if (dev->decoder == PHILIPS1)
352 pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353}
354
Hans Verkuil1ce79812009-11-25 12:37:00 -0300355static void pms_antisnow(struct pms *dev, short snow)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300357 if (dev->decoder == PHILIPS2)
358 pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
359 else if (dev->decoder == PHILIPS1)
360 pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361}
362
Hans Verkuil1ce79812009-11-25 12:37:00 -0300363static void pms_sharpness(struct pms *dev, short sharp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300365 if (dev->decoder == PHILIPS2)
366 pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
367 else if (dev->decoder == PHILIPS1)
368 pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369}
370
Hans Verkuil1ce79812009-11-25 12:37:00 -0300371static void pms_chromaagc(struct pms *dev, short agc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300373 if (dev->decoder == PHILIPS2)
374 pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
375 else if (dev->decoder == PHILIPS1)
376 pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377}
378
Hans Verkuil1ce79812009-11-25 12:37:00 -0300379static void pms_vertnoise(struct pms *dev, short noise)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300381 if (dev->decoder == PHILIPS2)
382 pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
383 else if (dev->decoder == PHILIPS1)
384 pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385}
386
Hans Verkuil1ce79812009-11-25 12:37:00 -0300387static void pms_forcecolour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300389 if (dev->decoder == PHILIPS2)
390 pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
391 else if (dev->decoder == PHILIPS1)
392 pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393}
394
Hans Verkuil1ce79812009-11-25 12:37:00 -0300395static void pms_antigamma(struct pms *dev, short gamma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300397 if (dev->decoder == PHILIPS2)
398 pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
399 else if (dev->decoder == PHILIPS1)
400 pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401}
402
Hans Verkuil1ce79812009-11-25 12:37:00 -0300403static void pms_prefilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300405 if (dev->decoder == PHILIPS2)
406 pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
407 else if (dev->decoder == PHILIPS1)
408 pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409}
410
Hans Verkuil1ce79812009-11-25 12:37:00 -0300411static void pms_hfilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300413 if (dev->decoder == PHILIPS2)
414 pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
415 else if (dev->decoder == PHILIPS1)
416 pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417}
418
Hans Verkuil1ce79812009-11-25 12:37:00 -0300419static void pms_vfilter(struct pms *dev, short filter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300421 if (dev->decoder == PHILIPS2)
422 pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
423 else if (dev->decoder == PHILIPS1)
424 pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425}
426
Hans Verkuil1ce79812009-11-25 12:37:00 -0300427static void pms_killcolour(struct pms *dev, short colour)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300429 if (dev->decoder == PHILIPS2) {
430 pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
431 pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
432 } else if (dev->decoder == PHILIPS1) {
433 pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
434 pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 }
436}
437
Hans Verkuil1ce79812009-11-25 12:37:00 -0300438static void pms_chromagain(struct pms *dev, short chroma)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300440 if (dev->decoder == PHILIPS2)
441 pms_i2c_write(dev, 0x8a, 0x11, chroma);
442 else if (dev->decoder == PHILIPS1)
443 pms_i2c_write(dev, 0x42, 0x11, chroma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444}
445
446
Hans Verkuil1ce79812009-11-25 12:37:00 -0300447static void pms_spacialcompl(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300449 mvv_write(dev, 0x3b, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450}
451
Hans Verkuil1ce79812009-11-25 12:37:00 -0300452static void pms_spacialcomph(struct pms *dev, short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300454 mvv_write(dev, 0x3a, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455}
456
Hans Verkuil1ce79812009-11-25 12:37:00 -0300457static void pms_vstart(struct pms *dev, short start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300459 mvv_write(dev, 0x16, start);
460 mvv_write(dev, 0x17, (start >> 8) & 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461}
462
463#endif
464
Hans Verkuil1ce79812009-11-25 12:37:00 -0300465static void pms_secamcross(struct pms *dev, short cross)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300467 if (dev->decoder == PHILIPS2)
468 pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
469 else if (dev->decoder == PHILIPS1)
470 pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
473
Hans Verkuil1ce79812009-11-25 12:37:00 -0300474static void pms_swsense(struct pms *dev, short sense)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300476 if (dev->decoder == PHILIPS2) {
477 pms_i2c_write(dev, 0x8a, 0x0a, sense);
478 pms_i2c_write(dev, 0x8a, 0x0b, sense);
479 } else if (dev->decoder == PHILIPS1) {
480 pms_i2c_write(dev, 0x42, 0x0a, sense);
481 pms_i2c_write(dev, 0x42, 0x0b, sense);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 }
483}
484
485
Hans Verkuil1ce79812009-11-25 12:37:00 -0300486static void pms_framerate(struct pms *dev, short frr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300488 int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300489
490 if (frr == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 return;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300492 fps = fps/frr;
493 mvv_write(dev, 0x14, 0x80 | fps);
494 mvv_write(dev, 0x15, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495}
496
Hans Verkuil1ce79812009-11-25 12:37:00 -0300497static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300499 mvv_write(dev, 0x1c, deciden); /* Denominator */
500 mvv_write(dev, 0x1d, decinum); /* Numerator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501}
502
503/*
504 * Turn 16bit ratios into best small ratio the chipset can grok
505 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300506
Hans Verkuil1ce79812009-11-25 12:37:00 -0300507static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300509 /* Knock it down by / 5 once */
510 if (decinum % 5 == 0) {
511 deciden /= 5;
512 decinum /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 }
514 /*
515 * 3's
516 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300517 while (decinum % 3 == 0 && deciden % 3 == 0) {
518 deciden /= 3;
519 decinum /= 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 }
521 /*
522 * 2's
523 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300524 while (decinum % 2 == 0 && deciden % 2 == 0) {
525 decinum /= 2;
526 deciden /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 }
528 /*
529 * Fudgyify
530 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300531 while (deciden > 32) {
532 deciden /= 2;
533 decinum = (decinum + 1) / 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300535 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 deciden--;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300537 pms_vert(dev, deciden, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538}
539
Hans Verkuil1ce79812009-11-25 12:37:00 -0300540static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300542 if (decinum <= 512) {
543 if (decinum % 5 == 0) {
544 decinum /= 5;
545 deciden /= 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300547 } else {
548 decinum = 512;
549 deciden = 640; /* 768 would be ideal */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300551
Hans Verkuil1ce79812009-11-25 12:37:00 -0300552 while (((decinum | deciden) & 1) == 0) {
553 decinum >>= 1;
554 deciden >>= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300556 while (deciden > 32) {
557 deciden >>= 1;
558 decinum = (decinum + 1) >> 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300560 if (deciden == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 deciden--;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300562
Hans Verkuil1ce79812009-11-25 12:37:00 -0300563 mvv_write(dev, 0x24, 0x80 | deciden);
564 mvv_write(dev, 0x25, decinum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
Hans Verkuil1ce79812009-11-25 12:37:00 -0300567static void pms_resolution(struct pms *dev, short width, short height)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
569 int fg_height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300570
Hans Verkuil1ce79812009-11-25 12:37:00 -0300571 fg_height = height;
572 if (fg_height > 280)
573 fg_height = 280;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300574
Hans Verkuil1ce79812009-11-25 12:37:00 -0300575 mvv_write(dev, 0x18, fg_height);
576 mvv_write(dev, 0x19, fg_height >> 8);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300577
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300578 if (dev->std & V4L2_STD_525_60) {
Hans Verkuil1ce79812009-11-25 12:37:00 -0300579 mvv_write(dev, 0x1a, 0xfc);
580 mvv_write(dev, 0x1b, 0x00);
581 if (height > fg_height)
582 pms_vertdeci(dev, 240, 240);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300584 pms_vertdeci(dev, fg_height, 240);
585 } else {
586 mvv_write(dev, 0x1a, 0x1a);
587 mvv_write(dev, 0x1b, 0x01);
588 if (fg_height > 256)
589 pms_vertdeci(dev, 270, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300591 pms_vertdeci(dev, fg_height, 270);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300593 mvv_write(dev, 0x12, 0);
594 mvv_write(dev, 0x13, MVVMEMORYWIDTH);
595 mvv_write(dev, 0x42, 0x00);
596 mvv_write(dev, 0x43, 0x00);
597 mvv_write(dev, 0x44, MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300598
Hans Verkuil1ce79812009-11-25 12:37:00 -0300599 mvv_write(dev, 0x22, width + 8);
600 mvv_write(dev, 0x23, (width + 8) >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300602 if (dev->std & V4L2_STD_525_60)
Hans Verkuil1ce79812009-11-25 12:37:00 -0300603 pms_horzdeci(dev, width, 640);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300605 pms_horzdeci(dev, width + 8, 768);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
Hans Verkuil1ce79812009-11-25 12:37:00 -0300607 mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
608 mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
609 mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
610 mvv_write(dev, 0x32, 0x00);
611 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612}
613
614
615/*
616 * Set Input
617 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300618
Hans Verkuil1ce79812009-11-25 12:37:00 -0300619static void pms_vcrinput(struct pms *dev, short input)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300621 if (dev->decoder == PHILIPS2)
622 pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
623 else if (dev->decoder == PHILIPS1)
624 pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625}
626
627
Hans Verkuil1ce79812009-11-25 12:37:00 -0300628static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629{
630 int y;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300631 int dw = 2 * dev->width;
Mauro Carvalho Chehab64e01cb2014-09-24 15:35:55 -0300632 char *tmp; /* using a temp buffer is faster than direct */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 int cnt = 0;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300634 int len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 unsigned char r8 = 0x5; /* value for reg8 */
636
Mauro Carvalho Chehab64e01cb2014-09-24 15:35:55 -0300637 tmp = kmalloc(dw + 32, GFP_KERNEL);
638 if (!tmp)
639 return 0;
640
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 if (rgb555)
642 r8 |= 0x20; /* else use untranslated rgb = 565 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300643 mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300646
Hans Verkuil1ce79812009-11-25 12:37:00 -0300647 for (y = 0; y < dev->height; y++) {
648 writeb(0, dev->mem); /* synchronisiert neue Zeile */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300649
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 /*
651 * This is in truth a fifo, be very careful as if you
652 * forgot this odd things will occur 8)
653 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300654
Hans Verkuil1ce79812009-11-25 12:37:00 -0300655 memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 cnt -= dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300657 while (cnt <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 /*
659 * Don't copy too far
660 */
Hans Verkuil1ce79812009-11-25 12:37:00 -0300661 int dt = dw;
662 if (dt + len > count)
663 dt = count - len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 cnt += dev->height;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300665 if (copy_to_user(buf, tmp + 32, dt))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 return len ? len : -EFAULT;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300667 buf += dt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 len += dt;
669 }
670 }
Mauro Carvalho Chehab64e01cb2014-09-24 15:35:55 -0300671 kfree(tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 return len;
673}
674
675
676/*
677 * Video4linux interfacing
678 */
679
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300680static int pms_querycap(struct file *file, void *priv,
681 struct v4l2_capability *vcap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300683 struct pms *dev = video_drvdata(file);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300684
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300685 strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
686 strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
Hans Verkuil4b255242012-05-06 10:41:54 -0300687 snprintf(vcap->bus_info, sizeof(vcap->bus_info),
688 "ISA:%s", dev->v4l2_dev.name);
689 vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
690 vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 return 0;
692}
693
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300694static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695{
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300696 static const char *inputs[4] = {
697 "Composite",
698 "S-Video",
699 "Composite (VCR)",
700 "S-Video (VCR)"
701 };
702
703 if (vin->index > 3)
704 return -EINVAL;
705 strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
706 vin->type = V4L2_INPUT_TYPE_CAMERA;
707 vin->audioset = 0;
708 vin->tuner = 0;
709 vin->std = V4L2_STD_ALL;
710 vin->status = 0;
711 return 0;
712}
713
714static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
715{
716 struct pms *dev = video_drvdata(file);
717
718 *inp = dev->input;
719 return 0;
720}
721
722static int pms_s_input(struct file *file, void *fh, unsigned int inp)
723{
724 struct pms *dev = video_drvdata(file);
725
726 if (inp > 3)
727 return -EINVAL;
728
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300729 dev->input = inp;
730 pms_videosource(dev, inp & 1);
731 pms_vcrinput(dev, inp >> 1);
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300732 return 0;
733}
734
735static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
736{
737 struct pms *dev = video_drvdata(file);
738
739 *std = dev->std;
740 return 0;
741}
742
Hans Verkuil314527a2013-03-15 06:10:40 -0300743static int pms_s_std(struct file *file, void *fh, v4l2_std_id std)
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300744{
745 struct pms *dev = video_drvdata(file);
746 int ret = 0;
747
Hans Verkuil314527a2013-03-15 06:10:40 -0300748 dev->std = std;
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300749 if (dev->std & V4L2_STD_NTSC) {
750 pms_framerate(dev, 30);
751 pms_secamcross(dev, 0);
752 pms_format(dev, 1);
753 } else if (dev->std & V4L2_STD_PAL) {
754 pms_framerate(dev, 25);
755 pms_secamcross(dev, 0);
756 pms_format(dev, 2);
757 } else if (dev->std & V4L2_STD_SECAM) {
758 pms_framerate(dev, 25);
759 pms_secamcross(dev, 1);
760 pms_format(dev, 2);
761 } else {
762 ret = -EINVAL;
763 }
764 /*
765 switch (v->mode) {
766 case VIDEO_MODE_AUTO:
767 pms_framerate(dev, 25);
768 pms_secamcross(dev, 0);
769 pms_format(dev, 0);
770 break;
771 }*/
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300772 return ret;
773}
774
Hans Verkuil4b255242012-05-06 10:41:54 -0300775static int pms_s_ctrl(struct v4l2_ctrl *ctrl)
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300776{
Hans Verkuil4b255242012-05-06 10:41:54 -0300777 struct pms *dev = container_of(ctrl->handler, struct pms, hdl);
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300778 int ret = 0;
779
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300780 switch (ctrl->id) {
781 case V4L2_CID_BRIGHTNESS:
Hans Verkuil4b255242012-05-06 10:41:54 -0300782 pms_brightness(dev, ctrl->val);
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300783 break;
784 case V4L2_CID_CONTRAST:
Hans Verkuil4b255242012-05-06 10:41:54 -0300785 pms_contrast(dev, ctrl->val);
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300786 break;
787 case V4L2_CID_SATURATION:
Hans Verkuil4b255242012-05-06 10:41:54 -0300788 pms_saturation(dev, ctrl->val);
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300789 break;
790 case V4L2_CID_HUE:
Hans Verkuil4b255242012-05-06 10:41:54 -0300791 pms_hue(dev, ctrl->val);
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300792 break;
793 default:
794 ret = -EINVAL;
795 break;
796 }
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300797 return ret;
798}
799
800static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
801{
802 struct pms *dev = video_drvdata(file);
803 struct v4l2_pix_format *pix = &fmt->fmt.pix;
804
805 pix->width = dev->width;
806 pix->height = dev->height;
807 pix->pixelformat = dev->width == 15 ?
808 V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
809 pix->field = V4L2_FIELD_NONE;
810 pix->bytesperline = 2 * dev->width;
811 pix->sizeimage = 2 * dev->width * dev->height;
812 /* Just a guess */
813 pix->colorspace = V4L2_COLORSPACE_SRGB;
814 return 0;
815}
816
817static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
818{
819 struct v4l2_pix_format *pix = &fmt->fmt.pix;
820
821 if (pix->height < 16 || pix->height > 480)
822 return -EINVAL;
823 if (pix->width < 16 || pix->width > 640)
824 return -EINVAL;
825 if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
826 pix->pixelformat != V4L2_PIX_FMT_RGB565)
827 return -EINVAL;
828 pix->field = V4L2_FIELD_NONE;
829 pix->bytesperline = 2 * pix->width;
830 pix->sizeimage = 2 * pix->width * pix->height;
831 /* Just a guess */
832 pix->colorspace = V4L2_COLORSPACE_SRGB;
833 return 0;
834}
835
836static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
837{
838 struct pms *dev = video_drvdata(file);
839 struct v4l2_pix_format *pix = &fmt->fmt.pix;
840 int ret = pms_try_fmt_vid_cap(file, fh, fmt);
841
842 if (ret)
843 return ret;
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300844 dev->width = pix->width;
845 dev->height = pix->height;
846 dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
847 pms_resolution(dev, dev->width, dev->height);
848 /* Ok we figured out what to use from our wide choice */
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300849 return 0;
850}
851
852static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
853{
854 static struct v4l2_fmtdesc formats[] = {
855 { 0, 0, 0,
856 "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
857 { 0, 0, 0, 0 }
858 },
Hans Verkuil4b255242012-05-06 10:41:54 -0300859 { 1, 0, 0,
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300860 "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
861 { 0, 0, 0, 0 }
862 },
863 };
864 enum v4l2_buf_type type = fmt->type;
865
866 if (fmt->index > 1)
867 return -EINVAL;
868
869 *fmt = formats[fmt->index];
870 fmt->type = type;
871 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872}
873
874static ssize_t pms_read(struct file *file, char __user *buf,
875 size_t count, loff_t *ppos)
876{
Hans Verkuil1ce79812009-11-25 12:37:00 -0300877 struct pms *dev = video_drvdata(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 int len;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300879
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300880 len = pms_capture(dev, buf, (dev->depth == 15), count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 return len;
882}
883
Hans Verkuil4b255242012-05-06 10:41:54 -0300884static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait)
885{
886 struct v4l2_fh *fh = file->private_data;
887 unsigned int res = POLLIN | POLLRDNORM;
888
889 if (v4l2_event_pending(fh))
890 res |= POLLPRI;
891 poll_wait(file, &fh->wait, wait);
892 return res;
893}
894
Hans Verkuilbec43662008-12-30 06:58:20 -0300895static const struct v4l2_file_operations pms_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 .owner = THIS_MODULE,
Hans Verkuil4b255242012-05-06 10:41:54 -0300897 .open = v4l2_fh_open,
898 .release = v4l2_fh_release,
899 .poll = pms_poll,
Hans Verkuil61df3c92010-11-14 10:09:38 -0300900 .unlocked_ioctl = video_ioctl2,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 .read = pms_read,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902};
903
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300904static const struct v4l2_ioctl_ops pms_ioctl_ops = {
Hans Verkuil4b255242012-05-06 10:41:54 -0300905 .vidioc_querycap = pms_querycap,
906 .vidioc_g_input = pms_g_input,
907 .vidioc_s_input = pms_s_input,
908 .vidioc_enum_input = pms_enum_input,
909 .vidioc_g_std = pms_g_std,
910 .vidioc_s_std = pms_s_std,
911 .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap,
912 .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap,
913 .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap,
914 .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap,
915 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
916 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
Hans Verkuilfeba2f82009-11-25 12:47:02 -0300917};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
919/*
920 * Probe for and initialise the Mediavision PMS
921 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300922
Hans Verkuil1ce79812009-11-25 12:37:00 -0300923static int init_mediavision(struct pms *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 int idec, decst;
926 int i;
Hans Verkuil1ce79812009-11-25 12:37:00 -0300927 static const unsigned char i2c_defs[] = {
928 0x4c, 0x30, 0x00, 0xe8,
929 0xb6, 0xe2, 0x00, 0x00,
930 0xff, 0xff, 0x00, 0x00,
931 0x00, 0x00, 0x78, 0x98,
932 0x00, 0x00, 0x00, 0x00,
933 0x34, 0x0a, 0xf4, 0xce,
934 0xe4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 };
936
Hans Verkuil1ce79812009-11-25 12:37:00 -0300937 dev->mem = ioremap(mem_base, 0x800);
938 if (!dev->mem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 return -ENOMEM;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300940
Hans Verkuil1ce79812009-11-25 12:37:00 -0300941 if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
942 printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
943 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 return -EBUSY;
945 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300946 if (!request_region(dev->io, 3, "Mediavision PMS")) {
947 printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
948 release_region(0x9a01, 1);
949 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 return -EBUSY;
951 }
Hans Verkuil1ce79812009-11-25 12:37:00 -0300952 outb(0xb8, 0x9a01); /* Unlock */
953 outb(dev->io >> 4, 0x9a01); /* Set IO port */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300954
955
Hans Verkuil1ce79812009-11-25 12:37:00 -0300956 decst = pms_i2c_stat(dev, 0x43);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300957
Hans Verkuil1ce79812009-11-25 12:37:00 -0300958 if (decst != -1)
959 idec = 2;
960 else if (pms_i2c_stat(dev, 0xb9) != -1)
961 idec = 3;
962 else if (pms_i2c_stat(dev, 0x8b) != -1)
963 idec = 1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300964 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300965 idec = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
967 printk(KERN_INFO "PMS type is %d\n", idec);
Hans Verkuil1ce79812009-11-25 12:37:00 -0300968 if (idec == 0) {
969 release_region(dev->io, 3);
970 release_region(0x9a01, 1);
971 iounmap(dev->mem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 return -ENODEV;
973 }
974
975 /*
976 * Ok we have a PMS of some sort
977 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300978
Hans Verkuil1ce79812009-11-25 12:37:00 -0300979 mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300980
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 /* Ok now load the defaults */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300982
Hans Verkuil1ce79812009-11-25 12:37:00 -0300983 for (i = 0; i < 0x19; i++) {
984 if (i2c_defs[i] == 0xff)
985 pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 else
Hans Verkuil1ce79812009-11-25 12:37:00 -0300987 pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300989
Hans Verkuil1ce79812009-11-25 12:37:00 -0300990 pms_i2c_write(dev, 0xb8, 0x00, 0x12);
991 pms_i2c_write(dev, 0xb8, 0x04, 0x00);
992 pms_i2c_write(dev, 0xb8, 0x07, 0x00);
993 pms_i2c_write(dev, 0xb8, 0x08, 0x00);
994 pms_i2c_write(dev, 0xb8, 0x09, 0xff);
995 pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
996 pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
997 pms_i2c_write(dev, 0xb8, 0x10, 0x03);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300998
Hans Verkuil1ce79812009-11-25 12:37:00 -0300999 mvv_write(dev, 0x01, 0x00);
1000 mvv_write(dev, 0x05, 0xa0);
1001 mvv_write(dev, 0x08, 0x25);
1002 mvv_write(dev, 0x09, 0x00);
1003 mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001004
Hans Verkuil1ce79812009-11-25 12:37:00 -03001005 mvv_write(dev, 0x10, 0x02);
1006 mvv_write(dev, 0x1e, 0x0c);
1007 mvv_write(dev, 0x1f, 0x03);
1008 mvv_write(dev, 0x26, 0x06);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001009
Hans Verkuil1ce79812009-11-25 12:37:00 -03001010 mvv_write(dev, 0x2b, 0x00);
1011 mvv_write(dev, 0x2c, 0x20);
1012 mvv_write(dev, 0x2d, 0x00);
1013 mvv_write(dev, 0x2f, 0x70);
1014 mvv_write(dev, 0x32, 0x00);
1015 mvv_write(dev, 0x33, MVVMEMORYWIDTH);
1016 mvv_write(dev, 0x34, 0x00);
1017 mvv_write(dev, 0x35, 0x00);
1018 mvv_write(dev, 0x3a, 0x80);
1019 mvv_write(dev, 0x3b, 0x10);
1020 mvv_write(dev, 0x20, 0x00);
1021 mvv_write(dev, 0x21, 0x00);
1022 mvv_write(dev, 0x30, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 return 0;
1024}
1025
1026/*
1027 * Initialization and module stuff
1028 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001029
Rene Hermanb54ff932008-09-03 16:48:21 -03001030#ifndef MODULE
1031static int enable;
1032module_param(enable, int, 0);
1033#endif
1034
Hans Verkuil4b255242012-05-06 10:41:54 -03001035static const struct v4l2_ctrl_ops pms_ctrl_ops = {
1036 .s_ctrl = pms_s_ctrl,
1037};
1038
1039static int pms_probe(struct device *pdev, unsigned int card)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040{
Hans Verkuil4b255242012-05-06 10:41:54 -03001041 struct pms *dev;
1042 struct v4l2_device *v4l2_dev;
1043 struct v4l2_ctrl_handler *hdl;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001044 int res;
1045
Rene Hermanb54ff932008-09-03 16:48:21 -03001046#ifndef MODULE
1047 if (!enable) {
Hans Verkuil4b255242012-05-06 10:41:54 -03001048 pr_err("PMS: not enabled, use pms.enable=1 to probe\n");
Rene Hermanb54ff932008-09-03 16:48:21 -03001049 return -ENODEV;
1050 }
1051#endif
1052
Hans Verkuil4b255242012-05-06 10:41:54 -03001053 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1054 if (dev == NULL)
1055 return -ENOMEM;
1056
Hans Verkuil1ce79812009-11-25 12:37:00 -03001057 dev->decoder = PHILIPS2;
1058 dev->io = io_port;
1059 dev->data = io_port + 1;
Hans Verkuil4b255242012-05-06 10:41:54 -03001060 v4l2_dev = &dev->v4l2_dev;
1061 hdl = &dev->hdl;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001062
Hans Verkuil4b255242012-05-06 10:41:54 -03001063 res = v4l2_device_register(pdev, v4l2_dev);
Hans Verkuil1ce79812009-11-25 12:37:00 -03001064 if (res < 0) {
1065 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
Hans Verkuil4b255242012-05-06 10:41:54 -03001066 goto free_dev;
1067 }
1068 v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n");
1069
1070 res = init_mediavision(dev);
1071 if (res) {
1072 v4l2_err(v4l2_dev, "Board not found.\n");
1073 goto free_io;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001074 }
1075
Hans Verkuil4b255242012-05-06 10:41:54 -03001076 v4l2_ctrl_handler_init(hdl, 4);
1077 v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
1078 V4L2_CID_BRIGHTNESS, 0, 255, 1, 139);
1079 v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
1080 V4L2_CID_CONTRAST, 0, 255, 1, 70);
1081 v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
1082 V4L2_CID_SATURATION, 0, 255, 1, 64);
1083 v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
1084 V4L2_CID_HUE, 0, 255, 1, 0);
1085 if (hdl->error) {
1086 res = hdl->error;
1087 goto free_hdl;
1088 }
1089
1090 mutex_init(&dev->lock);
Hans Verkuil1ce79812009-11-25 12:37:00 -03001091 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
1092 dev->vdev.v4l2_dev = v4l2_dev;
Hans Verkuil4b255242012-05-06 10:41:54 -03001093 dev->vdev.ctrl_handler = hdl;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001094 dev->vdev.fops = &pms_fops;
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001095 dev->vdev.ioctl_ops = &pms_ioctl_ops;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001096 dev->vdev.release = video_device_release_empty;
Hans Verkuil4b255242012-05-06 10:41:54 -03001097 dev->vdev.lock = &dev->lock;
1098 dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001099 video_set_drvdata(&dev->vdev, dev);
Hans Verkuilfeba2f82009-11-25 12:47:02 -03001100 dev->std = V4L2_STD_NTSC_M;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001101 dev->height = 240;
1102 dev->width = 320;
Hans Verkuil4b255242012-05-06 10:41:54 -03001103 dev->depth = 16;
Hans Verkuil1ce79812009-11-25 12:37:00 -03001104 pms_swsense(dev, 75);
1105 pms_resolution(dev, 320, 240);
1106 pms_videosource(dev, 0);
1107 pms_vcrinput(dev, 0);
Hans Verkuil4b255242012-05-06 10:41:54 -03001108 v4l2_ctrl_handler_setup(hdl);
1109 res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr);
1110 if (res >= 0)
1111 return 0;
1112
1113free_hdl:
1114 v4l2_ctrl_handler_free(hdl);
1115 v4l2_device_unregister(&dev->v4l2_dev);
1116free_io:
1117 release_region(dev->io, 3);
1118 release_region(0x9a01, 1);
1119 iounmap(dev->mem);
1120free_dev:
1121 kfree(dev);
1122 return res;
1123}
1124
1125static int pms_remove(struct device *pdev, unsigned int card)
1126{
1127 struct pms *dev = dev_get_drvdata(pdev);
1128
1129 video_unregister_device(&dev->vdev);
1130 v4l2_ctrl_handler_free(&dev->hdl);
1131 release_region(dev->io, 3);
1132 release_region(0x9a01, 1);
1133 iounmap(dev->mem);
Hans Verkuil1ce79812009-11-25 12:37:00 -03001134 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135}
1136
Hans Verkuil4b255242012-05-06 10:41:54 -03001137static struct isa_driver pms_driver = {
1138 .probe = pms_probe,
1139 .remove = pms_remove,
1140 .driver = {
1141 .name = "pms",
1142 },
1143};
1144
1145static int __init pms_init(void)
1146{
1147 return isa_register_driver(&pms_driver, 1);
1148}
1149
Hans Verkuil1ce79812009-11-25 12:37:00 -03001150static void __exit pms_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151{
Hans Verkuil4b255242012-05-06 10:41:54 -03001152 isa_unregister_driver(&pms_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153}
1154
Hans Verkuil1ce79812009-11-25 12:37:00 -03001155module_init(pms_init);
1156module_exit(pms_exit);