blob: 51b1461d8fb62707f48482a59ee34cef55d85a50 [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.
13 * His driver works so send any reports to alan@redhat.com unless the
14 * userspace driver also doesn't work for you...
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030015 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 * Changes:
17 * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030018 * - pms_capture: report back -EFAULT
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 */
20
21#include <linux/module.h>
22#include <linux/delay.h>
23#include <linux/errno.h>
24#include <linux/fs.h>
25#include <linux/kernel.h>
26#include <linux/slab.h>
27#include <linux/mm.h>
28#include <linux/ioport.h>
29#include <linux/init.h>
30#include <asm/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/videodev.h>
Mauro Carvalho Chehab5e87efa2006-06-05 10:26:32 -030032#include <media/v4l2-common.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020033#include <linux/mutex.h>
34
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <asm/uaccess.h>
36
37
38#define MOTOROLA 1
39#define PHILIPS2 2
40#define PHILIPS1 3
41#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
42
43struct pms_device
44{
45 struct video_device v;
46 struct video_picture picture;
47 int height;
48 int width;
Ingo Molnar3593cab2006-02-07 06:49:14 -020049 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -070050};
51
52struct i2c_info
53{
54 u8 slave;
55 u8 sub;
56 u8 data;
57 u8 hits;
58};
59
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030060static int i2c_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061static struct i2c_info i2cinfo[64];
62
63static int decoder = PHILIPS2;
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030064static int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66/*
67 * I/O ports and Shared Memory
68 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030069
Linus Torvalds1da177e2005-04-16 15:20:36 -070070static int io_port = 0x250;
71static int data_port = 0x251;
72static int mem_base = 0xC8000;
73static void __iomem *mem;
74static int video_nr = -1;
75
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030076
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78static inline void mvv_write(u8 index, u8 value)
79{
80 outw(index|(value<<8), io_port);
81}
82
83static inline u8 mvv_read(u8 index)
84{
85 outb(index, io_port);
86 return inb(data_port);
87}
88
89static int pms_i2c_stat(u8 slave)
90{
91 int counter;
92 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030093
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 outb(0x28, io_port);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030095
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 counter=0;
97 while((inb(data_port)&0x01)==0)
98 if(counter++==256)
99 break;
100
101 while((inb(data_port)&0x01)!=0)
102 if(counter++==256)
103 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300104
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 outb(slave, io_port);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 counter=0;
108 while((inb(data_port)&0x01)==0)
109 if(counter++==256)
110 break;
111
112 while((inb(data_port)&0x01)!=0)
113 if(counter++==256)
114 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300115
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 for(i=0;i<12;i++)
117 {
118 char st=inb(data_port);
119 if((st&2)!=0)
120 return -1;
121 if((st&1)==0)
122 break;
123 }
124 outb(0x29, io_port);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300125 return inb(data_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126}
127
128static int pms_i2c_write(u16 slave, u16 sub, u16 data)
129{
130 int skip=0;
131 int count;
132 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300133
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 for(i=0;i<i2c_count;i++)
135 {
136 if((i2cinfo[i].slave==slave) &&
137 (i2cinfo[i].sub == sub))
138 {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300139 if(i2cinfo[i].data==data)
140 skip=1;
141 i2cinfo[i].data=data;
142 i=i2c_count+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 }
144 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300145
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 if(i==i2c_count && i2c_count<64)
147 {
148 i2cinfo[i2c_count].slave=slave;
149 i2cinfo[i2c_count].sub=sub;
150 i2cinfo[i2c_count].data=data;
151 i2c_count++;
152 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 if(skip)
155 return 0;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300156
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 mvv_write(0x29, sub);
158 mvv_write(0x2A, data);
159 mvv_write(0x28, slave);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300160
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 outb(0x28, io_port);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 count=0;
164 while((inb(data_port)&1)==0)
165 if(count>255)
166 break;
167 while((inb(data_port)&1)!=0)
168 if(count>255)
169 break;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 count=inb(data_port);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300172
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 if(count&2)
174 return -1;
175 return count;
176}
177
178static int pms_i2c_read(int slave, int sub)
179{
180 int i=0;
181 for(i=0;i<i2c_count;i++)
182 {
183 if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub)
184 return i2cinfo[i].data;
185 }
186 return 0;
187}
188
189
190static void pms_i2c_andor(int slave, int sub, int and, int or)
191{
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300192 u8 tmp;
193
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 tmp=pms_i2c_read(slave, sub);
195 tmp = (tmp&and)|or;
196 pms_i2c_write(slave, sub, tmp);
197}
198
199/*
200 * Control functions
201 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300202
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203
204static void pms_videosource(short source)
205{
206 mvv_write(0x2E, source?0x31:0x30);
207}
208
209static void pms_hue(short hue)
210{
211 switch(decoder)
212 {
213 case MOTOROLA:
214 pms_i2c_write(0x8A, 0x00, hue);
215 break;
216 case PHILIPS2:
217 pms_i2c_write(0x8A, 0x07, hue);
218 break;
219 case PHILIPS1:
220 pms_i2c_write(0x42, 0x07, hue);
221 break;
222 }
223}
224
225static void pms_colour(short colour)
226{
227 switch(decoder)
228 {
229 case MOTOROLA:
230 pms_i2c_write(0x8A, 0x00, colour);
231 break;
232 case PHILIPS1:
233 pms_i2c_write(0x42, 0x12, colour);
234 break;
235 }
236}
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300237
238
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239static void pms_contrast(short contrast)
240{
241 switch(decoder)
242 {
243 case MOTOROLA:
244 pms_i2c_write(0x8A, 0x00, contrast);
245 break;
246 case PHILIPS1:
247 pms_i2c_write(0x42, 0x13, contrast);
248 break;
249 }
250}
251
252static void pms_brightness(short brightness)
253{
254 switch(decoder)
255 {
256 case MOTOROLA:
257 pms_i2c_write(0x8A, 0x00, brightness);
258 pms_i2c_write(0x8A, 0x00, brightness);
259 pms_i2c_write(0x8A, 0x00, brightness);
260 break;
261 case PHILIPS1:
262 pms_i2c_write(0x42, 0x19, brightness);
263 break;
264 }
265}
266
267
268static void pms_format(short format)
269{
270 int target;
271 standard = format;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300272
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 if(decoder==PHILIPS1)
274 target=0x42;
275 else if(decoder==PHILIPS2)
276 target=0x8A;
277 else
278 return;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300279
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 switch(format)
281 {
282 case 0: /* Auto */
283 pms_i2c_andor(target, 0x0D, 0xFE,0x00);
284 pms_i2c_andor(target, 0x0F, 0x3F,0x80);
285 break;
286 case 1: /* NTSC */
287 pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
288 pms_i2c_andor(target, 0x0F, 0x3F, 0x40);
289 break;
290 case 2: /* PAL */
291 pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
292 pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
293 break;
294 case 3: /* SECAM */
295 pms_i2c_andor(target, 0x0D, 0xFE, 0x01);
296 pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
297 break;
298 }
299}
300
301#ifdef FOR_FUTURE_EXPANSION
302
303/*
304 * These features of the PMS card are not currently exposes. They
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300305 * could become a private v4l ioctl for PMSCONFIG or somesuch if
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 * people need it. We also don't yet use the PMS interrupt.
307 */
308
309static void pms_hstart(short start)
310{
311 switch(decoder)
312 {
313 case PHILIPS1:
314 pms_i2c_write(0x8A, 0x05, start);
315 pms_i2c_write(0x8A, 0x18, start);
316 break;
317 case PHILIPS2:
318 pms_i2c_write(0x42, 0x05, start);
319 pms_i2c_write(0x42, 0x18, start);
320 break;
321 }
322}
323
324/*
325 * Bandpass filters
326 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300327
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328static void pms_bandpass(short pass)
329{
330 if(decoder==PHILIPS2)
331 pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4);
332 else if(decoder==PHILIPS1)
333 pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4);
334}
335
336static void pms_antisnow(short snow)
337{
338 if(decoder==PHILIPS2)
339 pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2);
340 else if(decoder==PHILIPS1)
341 pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2);
342}
343
344static void pms_sharpness(short sharp)
345{
346 if(decoder==PHILIPS2)
347 pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03);
348 else if(decoder==PHILIPS1)
349 pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03);
350}
351
352static void pms_chromaagc(short agc)
353{
354 if(decoder==PHILIPS2)
355 pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5);
356 else if(decoder==PHILIPS1)
357 pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5);
358}
359
360static void pms_vertnoise(short noise)
361{
362 if(decoder==PHILIPS2)
363 pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3);
364 else if(decoder==PHILIPS1)
365 pms_i2c_andor(0x42, 0x10, 0xFC, noise&3);
366}
367
368static void pms_forcecolour(short colour)
369{
370 if(decoder==PHILIPS2)
371 pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7);
372 else if(decoder==PHILIPS1)
373 pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7);
374}
375
376static void pms_antigamma(short gamma)
377{
378 if(decoder==PHILIPS2)
379 pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7);
380 else if(decoder==PHILIPS1)
381 pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7);
382}
383
384static void pms_prefilter(short filter)
385{
386 if(decoder==PHILIPS2)
387 pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6);
388 else if(decoder==PHILIPS1)
389 pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6);
390}
391
392static void pms_hfilter(short filter)
393{
394 if(decoder==PHILIPS2)
395 pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5);
396 else if(decoder==PHILIPS1)
397 pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5);
398}
399
400static void pms_vfilter(short filter)
401{
402 if(decoder==PHILIPS2)
403 pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5);
404 else if(decoder==PHILIPS1)
405 pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5);
406}
407
408static void pms_killcolour(short colour)
409{
410 if(decoder==PHILIPS2)
411 {
412 pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3);
413 pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3);
414 }
415 else if(decoder==PHILIPS1)
416 {
417 pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3);
418 pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3);
419 }
420}
421
422static void pms_chromagain(short chroma)
423{
424 if(decoder==PHILIPS2)
425 {
426 pms_i2c_write(0x8A, 0x11, chroma);
427 }
428 else if(decoder==PHILIPS1)
429 {
430 pms_i2c_write(0x42, 0x11, chroma);
431 }
432}
433
434
435static void pms_spacialcompl(short data)
436{
437 mvv_write(0x3B, data);
438}
439
440static void pms_spacialcomph(short data)
441{
442 mvv_write(0x3A, data);
443}
444
445static void pms_vstart(short start)
446{
447 mvv_write(0x16, start);
448 mvv_write(0x17, (start>>8)&0x01);
449}
450
451#endif
452
453static void pms_secamcross(short cross)
454{
455 if(decoder==PHILIPS2)
456 pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
457 else if(decoder==PHILIPS1)
458 pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
459}
460
461
462static void pms_swsense(short sense)
463{
464 if(decoder==PHILIPS2)
465 {
466 pms_i2c_write(0x8A, 0x0A, sense);
467 pms_i2c_write(0x8A, 0x0B, sense);
468 }
469 else if(decoder==PHILIPS1)
470 {
471 pms_i2c_write(0x42, 0x0A, sense);
472 pms_i2c_write(0x42, 0x0B, sense);
473 }
474}
475
476
477static void pms_framerate(short frr)
478{
479 int fps=(standard==1)?30:25;
480 if(frr==0)
481 return;
482 fps=fps/frr;
483 mvv_write(0x14,0x80|fps);
484 mvv_write(0x15,1);
485}
486
487static void pms_vert(u8 deciden, u8 decinum)
488{
489 mvv_write(0x1C, deciden); /* Denominator */
490 mvv_write(0x1D, decinum); /* Numerator */
491}
492
493/*
494 * Turn 16bit ratios into best small ratio the chipset can grok
495 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497static void pms_vertdeci(unsigned short decinum, unsigned short deciden)
498{
499 /* Knock it down by /5 once */
500 if(decinum%5==0)
501 {
502 deciden/=5;
503 decinum/=5;
504 }
505 /*
506 * 3's
507 */
508 while(decinum%3==0 && deciden%3==0)
509 {
510 deciden/=3;
511 decinum/=3;
512 }
513 /*
514 * 2's
515 */
516 while(decinum%2==0 && deciden%2==0)
517 {
518 decinum/=2;
519 deciden/=2;
520 }
521 /*
522 * Fudgyify
523 */
524 while(deciden>32)
525 {
526 deciden/=2;
527 decinum=(decinum+1)/2;
528 }
529 if(deciden==32)
530 deciden--;
531 pms_vert(deciden,decinum);
532}
533
534static void pms_horzdeci(short decinum, short deciden)
535{
536 if(decinum<=512)
537 {
538 if(decinum%5==0)
539 {
540 decinum/=5;
541 deciden/=5;
542 }
543 }
544 else
545 {
546 decinum=512;
547 deciden=640; /* 768 would be ideal */
548 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300549
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 while(((decinum|deciden)&1)==0)
551 {
552 decinum>>=1;
553 deciden>>=1;
554 }
555 while(deciden>32)
556 {
557 deciden>>=1;
558 decinum=(decinum+1)>>1;
559 }
560 if(deciden==32)
561 deciden--;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300562
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 mvv_write(0x24, 0x80|deciden);
564 mvv_write(0x25, decinum);
565}
566
567static void pms_resolution(short width, short height)
568{
569 int fg_height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300570
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 fg_height=height;
572 if(fg_height>280)
573 fg_height=280;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300574
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 mvv_write(0x18, fg_height);
576 mvv_write(0x19, fg_height>>8);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 if(standard==1)
579 {
580 mvv_write(0x1A, 0xFC);
581 mvv_write(0x1B, 0x00);
582 if(height>fg_height)
583 pms_vertdeci(240,240);
584 else
585 pms_vertdeci(fg_height,240);
586 }
587 else
588 {
589 mvv_write(0x1A, 0x1A);
590 mvv_write(0x1B, 0x01);
591 if(fg_height>256)
592 pms_vertdeci(270,270);
593 else
594 pms_vertdeci(fg_height, 270);
595 }
596 mvv_write(0x12,0);
597 mvv_write(0x13, MVVMEMORYWIDTH);
598 mvv_write(0x42, 0x00);
599 mvv_write(0x43, 0x00);
600 mvv_write(0x44, MVVMEMORYWIDTH);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 mvv_write(0x22, width+8);
603 mvv_write(0x23, (width+8)>> 8);
604
605 if(standard==1)
606 pms_horzdeci(width,640);
607 else
608 pms_horzdeci(width+8, 768);
609
610 mvv_write(0x30, mvv_read(0x30)&0xFE);
611 mvv_write(0x08, mvv_read(0x08)|0x01);
612 mvv_write(0x01, mvv_read(0x01)&0xFD);
613 mvv_write(0x32, 0x00);
614 mvv_write(0x33, MVVMEMORYWIDTH);
615}
616
617
618/*
619 * Set Input
620 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622static void pms_vcrinput(short input)
623{
624 if(decoder==PHILIPS2)
625 pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7);
626 else if(decoder==PHILIPS1)
627 pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7);
628}
629
630
631static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count)
632{
633 int y;
634 int dw = 2*dev->width;
635
636 char tmp[dw+32]; /* using a temp buffer is faster than direct */
637 int cnt = 0;
638 int len=0;
639 unsigned char r8 = 0x5; /* value for reg8 */
640
641 if (rgb555)
642 r8 |= 0x20; /* else use untranslated rgb = 565 */
643 mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */
644
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
647 for (y = 0; y < dev->height; y++ )
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 {
649 writeb(0, mem); /* synchronisiert neue Zeile */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300650
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 /*
652 * This is in truth a fifo, be very careful as if you
653 * forgot this odd things will occur 8)
654 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300655
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 memcpy_fromio(tmp, mem, dw+32); /* discard 16 word */
657 cnt -= dev->height;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300658 while (cnt <= 0)
659 {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 /*
661 * Don't copy too far
662 */
663 int dt=dw;
664 if(dt+len>count)
665 dt=count-len;
666 cnt += dev->height;
667 if (copy_to_user(buf, tmp+32, dt))
668 return len ? len : -EFAULT;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300669 buf += dt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 len += dt;
671 }
672 }
673 return len;
674}
675
676
677/*
678 * Video4linux interfacing
679 */
680
681static int pms_do_ioctl(struct inode *inode, struct file *file,
682 unsigned int cmd, void *arg)
683{
684 struct video_device *dev = video_devdata(file);
685 struct pms_device *pd=(struct pms_device *)dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300686
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 switch(cmd)
688 {
689 case VIDIOCGCAP:
690 {
691 struct video_capability *b = arg;
692 strcpy(b->name, "Mediavision PMS");
693 b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
694 b->channels = 4;
695 b->audios = 0;
696 b->maxwidth = 640;
697 b->maxheight = 480;
698 b->minwidth = 16;
699 b->minheight = 16;
700 return 0;
701 }
702 case VIDIOCGCHAN:
703 {
704 struct video_channel *v = arg;
705 if(v->channel<0 || v->channel>3)
706 return -EINVAL;
707 v->flags=0;
708 v->tuners=1;
709 /* Good question.. its composite or SVHS so.. */
710 v->type = VIDEO_TYPE_CAMERA;
711 switch(v->channel)
712 {
713 case 0:
714 strcpy(v->name, "Composite");break;
715 case 1:
716 strcpy(v->name, "SVideo");break;
717 case 2:
718 strcpy(v->name, "Composite(VCR)");break;
719 case 3:
720 strcpy(v->name, "SVideo(VCR)");break;
721 }
722 return 0;
723 }
724 case VIDIOCSCHAN:
725 {
726 struct video_channel *v = arg;
727 if(v->channel<0 || v->channel>3)
728 return -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -0200729 mutex_lock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 pms_videosource(v->channel&1);
731 pms_vcrinput(v->channel>>1);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200732 mutex_unlock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 return 0;
734 }
735 case VIDIOCGTUNER:
736 {
737 struct video_tuner *v = arg;
738 if(v->tuner)
739 return -EINVAL;
740 strcpy(v->name, "Format");
741 v->rangelow=0;
742 v->rangehigh=0;
743 v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
744 switch(standard)
745 {
746 case 0:
747 v->mode = VIDEO_MODE_AUTO;
748 break;
749 case 1:
750 v->mode = VIDEO_MODE_NTSC;
751 break;
752 case 2:
753 v->mode = VIDEO_MODE_PAL;
754 break;
755 case 3:
756 v->mode = VIDEO_MODE_SECAM;
757 break;
758 }
759 return 0;
760 }
761 case VIDIOCSTUNER:
762 {
763 struct video_tuner *v = arg;
764 if(v->tuner)
765 return -EINVAL;
Ingo Molnar3593cab2006-02-07 06:49:14 -0200766 mutex_lock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 switch(v->mode)
768 {
769 case VIDEO_MODE_AUTO:
770 pms_framerate(25);
771 pms_secamcross(0);
772 pms_format(0);
773 break;
774 case VIDEO_MODE_NTSC:
775 pms_framerate(30);
776 pms_secamcross(0);
777 pms_format(1);
778 break;
779 case VIDEO_MODE_PAL:
780 pms_framerate(25);
781 pms_secamcross(0);
782 pms_format(2);
783 break;
784 case VIDEO_MODE_SECAM:
785 pms_framerate(25);
786 pms_secamcross(1);
787 pms_format(2);
788 break;
789 default:
Ingo Molnar3593cab2006-02-07 06:49:14 -0200790 mutex_unlock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 return -EINVAL;
792 }
Ingo Molnar3593cab2006-02-07 06:49:14 -0200793 mutex_unlock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 return 0;
795 }
796 case VIDIOCGPICT:
797 {
798 struct video_picture *p = arg;
799 *p = pd->picture;
800 return 0;
801 }
802 case VIDIOCSPICT:
803 {
804 struct video_picture *p = arg;
805 if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16)
806 ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15)))
Trent Piepho657de3c2006-06-20 00:30:57 -0300807 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 pd->picture= *p;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300809
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 /*
811 * Now load the card.
812 */
813
Ingo Molnar3593cab2006-02-07 06:49:14 -0200814 mutex_lock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 pms_brightness(p->brightness>>8);
816 pms_hue(p->hue>>8);
817 pms_colour(p->colour>>8);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300818 pms_contrast(p->contrast>>8);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200819 mutex_unlock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 return 0;
821 }
822 case VIDIOCSWIN:
823 {
824 struct video_window *vw = arg;
825 if(vw->flags)
826 return -EINVAL;
827 if(vw->clipcount)
828 return -EINVAL;
829 if(vw->height<16||vw->height>480)
830 return -EINVAL;
831 if(vw->width<16||vw->width>640)
832 return -EINVAL;
833 pd->width=vw->width;
834 pd->height=vw->height;
Ingo Molnar3593cab2006-02-07 06:49:14 -0200835 mutex_lock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 pms_resolution(pd->width, pd->height);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200837 mutex_unlock(&pd->lock); /* Ok we figured out what to use from our wide choice */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 return 0;
839 }
840 case VIDIOCGWIN:
841 {
842 struct video_window *vw = arg;
843 memset(vw,0,sizeof(*vw));
844 vw->width=pd->width;
845 vw->height=pd->height;
846 return 0;
847 }
848 case VIDIOCKEY:
849 return 0;
850 case VIDIOCCAPTURE:
851 case VIDIOCGFBUF:
852 case VIDIOCSFBUF:
853 case VIDIOCGFREQ:
854 case VIDIOCSFREQ:
855 case VIDIOCGAUDIO:
856 case VIDIOCSAUDIO:
857 return -EINVAL;
858 default:
859 return -ENOIOCTLCMD;
860 }
861 return 0;
862}
863
864static int pms_ioctl(struct inode *inode, struct file *file,
865 unsigned int cmd, unsigned long arg)
866{
867 return video_usercopy(inode, file, cmd, arg, pms_do_ioctl);
868}
869
870static ssize_t pms_read(struct file *file, char __user *buf,
871 size_t count, loff_t *ppos)
872{
873 struct video_device *v = video_devdata(file);
874 struct pms_device *pd=(struct pms_device *)v;
875 int len;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300876
Ingo Molnar3593cab2006-02-07 06:49:14 -0200877 mutex_lock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200879 mutex_unlock(&pd->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 return len;
881}
882
Arjan van de Venfa027c22007-02-12 00:55:33 -0800883static const struct file_operations pms_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 .owner = THIS_MODULE,
885 .open = video_exclusive_open,
886 .release = video_exclusive_release,
887 .ioctl = pms_ioctl,
Douglas Schilling Landgraf078ff792008-04-22 14:46:11 -0300888#ifdef CONFIG_COMPAT
Arnd Bergmann0d0fbf82006-01-09 15:24:57 -0200889 .compat_ioctl = v4l_compat_ioctl32,
Douglas Schilling Landgraf078ff792008-04-22 14:46:11 -0300890#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 .read = pms_read,
892 .llseek = no_llseek,
893};
894
895static struct video_device pms_template=
896{
897 .owner = THIS_MODULE,
898 .name = "Mediavision PMS",
899 .type = VID_TYPE_CAPTURE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 .fops = &pms_fops,
901};
902
903static struct pms_device pms_device;
904
905
906/*
907 * Probe for and initialise the Mediavision PMS
908 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300909
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910static int init_mediavision(void)
911{
912 int id;
913 int idec, decst;
914 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300915
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 unsigned char i2c_defs[]={
917 0x4C,0x30,0x00,0xE8,
918 0xB6,0xE2,0x00,0x00,
919 0xFF,0xFF,0x00,0x00,
920 0x00,0x00,0x78,0x98,
921 0x00,0x00,0x00,0x00,
922 0x34,0x0A,0xF4,0xCE,
923 0xE4
924 };
925
926 mem = ioremap(mem_base, 0x800);
927 if (!mem)
928 return -ENOMEM;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300929
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 if (!request_region(0x9A01, 1, "Mediavision PMS config"))
931 {
932 printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n");
933 iounmap(mem);
934 return -EBUSY;
935 }
936 if (!request_region(io_port, 3, "Mediavision PMS"))
937 {
938 printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port);
939 release_region(0x9A01, 1);
940 iounmap(mem);
941 return -EBUSY;
942 }
943 outb(0xB8, 0x9A01); /* Unlock */
944 outb(io_port>>4, 0x9A01); /* Set IO port */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300945
946
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 id=mvv_read(3);
948 decst=pms_i2c_stat(0x43);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300949
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 if(decst!=-1)
951 idec=2;
952 else if(pms_i2c_stat(0xb9)!=-1)
953 idec=3;
954 else if(pms_i2c_stat(0x8b)!=-1)
955 idec=1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300956 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 idec=0;
958
959 printk(KERN_INFO "PMS type is %d\n", idec);
960 if(idec == 0) {
961 release_region(io_port, 3);
962 release_region(0x9A01, 1);
963 iounmap(mem);
964 return -ENODEV;
965 }
966
967 /*
968 * Ok we have a PMS of some sort
969 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300970
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 mvv_write(0x04, mem_base>>12); /* Set the memory area */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300972
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 /* Ok now load the defaults */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300974
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 for(i=0;i<0x19;i++)
976 {
977 if(i2c_defs[i]==0xFF)
978 pms_i2c_andor(0x8A, i, 0x07,0x00);
979 else
980 pms_i2c_write(0x8A, i, i2c_defs[i]);
981 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300982
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 pms_i2c_write(0xB8,0x00,0x12);
984 pms_i2c_write(0xB8,0x04,0x00);
985 pms_i2c_write(0xB8,0x07,0x00);
986 pms_i2c_write(0xB8,0x08,0x00);
987 pms_i2c_write(0xB8,0x09,0xFF);
988 pms_i2c_write(0xB8,0x0A,0x00);
989 pms_i2c_write(0xB8,0x0B,0x10);
990 pms_i2c_write(0xB8,0x10,0x03);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300991
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 mvv_write(0x01, 0x00);
993 mvv_write(0x05, 0xA0);
994 mvv_write(0x08, 0x25);
995 mvv_write(0x09, 0x00);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300996 mvv_write(0x0A, 0x20|MVVMEMORYWIDTH);
997
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 mvv_write(0x10, 0x02);
999 mvv_write(0x1E, 0x0C);
1000 mvv_write(0x1F, 0x03);
1001 mvv_write(0x26, 0x06);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001002
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 mvv_write(0x2B, 0x00);
1004 mvv_write(0x2C, 0x20);
1005 mvv_write(0x2D, 0x00);
1006 mvv_write(0x2F, 0x70);
1007 mvv_write(0x32, 0x00);
1008 mvv_write(0x33, MVVMEMORYWIDTH);
1009 mvv_write(0x34, 0x00);
1010 mvv_write(0x35, 0x00);
1011 mvv_write(0x3A, 0x80);
1012 mvv_write(0x3B, 0x10);
1013 mvv_write(0x20, 0x00);
1014 mvv_write(0x21, 0x00);
1015 mvv_write(0x30, 0x22);
1016 return 0;
1017}
1018
1019/*
1020 * Initialization and module stuff
1021 */
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001022
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023static int __init init_pms_cards(void)
1024{
1025 printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001026
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 data_port = io_port +1;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001028
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 if(init_mediavision())
1030 {
1031 printk(KERN_INFO "Board not found.\n");
1032 return -ENODEV;
1033 }
1034 memcpy(&pms_device, &pms_template, sizeof(pms_template));
Ingo Molnar3593cab2006-02-07 06:49:14 -02001035 mutex_init(&pms_device.lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 pms_device.height=240;
1037 pms_device.width=320;
1038 pms_swsense(75);
1039 pms_resolution(320,240);
1040 return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr);
1041}
1042
1043module_param(io_port, int, 0);
1044module_param(mem_base, int, 0);
1045module_param(video_nr, int, 0);
1046MODULE_LICENSE("GPL");
1047
1048
1049static void __exit shutdown_mediavision(void)
1050{
1051 release_region(io_port,3);
1052 release_region(0x9A01, 1);
1053}
1054
1055static void __exit cleanup_pms_module(void)
1056{
1057 shutdown_mediavision();
1058 video_unregister_device((struct video_device *)&pms_device);
1059 iounmap(mem);
1060}
1061
1062module_init(init_pms_cards);
1063module_exit(cleanup_pms_module);
1064