diff options
Diffstat (limited to 'drivers/media/video/pms.c')
-rw-r--r-- | drivers/media/video/pms.c | 1060 |
1 files changed, 1060 insertions, 0 deletions
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c new file mode 100644 index 000000000000..2504207b2e3d --- /dev/null +++ b/drivers/media/video/pms.c | |||
@@ -0,0 +1,1060 @@ | |||
1 | /* | ||
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... | ||
15 | * | ||
16 | * Changes: | ||
17 | * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it> | ||
18 | * - pms_capture: report back -EFAULT | ||
19 | */ | ||
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> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/videodev.h> | ||
33 | #include <asm/uaccess.h> | ||
34 | |||
35 | |||
36 | #define MOTOROLA 1 | ||
37 | #define PHILIPS2 2 | ||
38 | #define PHILIPS1 3 | ||
39 | #define MVVMEMORYWIDTH 0x40 /* 512 bytes */ | ||
40 | |||
41 | struct pms_device | ||
42 | { | ||
43 | struct video_device v; | ||
44 | struct video_picture picture; | ||
45 | int height; | ||
46 | int width; | ||
47 | struct semaphore lock; | ||
48 | }; | ||
49 | |||
50 | struct i2c_info | ||
51 | { | ||
52 | u8 slave; | ||
53 | u8 sub; | ||
54 | u8 data; | ||
55 | u8 hits; | ||
56 | }; | ||
57 | |||
58 | static int i2c_count = 0; | ||
59 | static struct i2c_info i2cinfo[64]; | ||
60 | |||
61 | static int decoder = PHILIPS2; | ||
62 | static int standard = 0; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ | ||
63 | |||
64 | /* | ||
65 | * I/O ports and Shared Memory | ||
66 | */ | ||
67 | |||
68 | static int io_port = 0x250; | ||
69 | static int data_port = 0x251; | ||
70 | static int mem_base = 0xC8000; | ||
71 | static void __iomem *mem; | ||
72 | static int video_nr = -1; | ||
73 | |||
74 | |||
75 | |||
76 | static inline void mvv_write(u8 index, u8 value) | ||
77 | { | ||
78 | outw(index|(value<<8), io_port); | ||
79 | } | ||
80 | |||
81 | static inline u8 mvv_read(u8 index) | ||
82 | { | ||
83 | outb(index, io_port); | ||
84 | return inb(data_port); | ||
85 | } | ||
86 | |||
87 | static int pms_i2c_stat(u8 slave) | ||
88 | { | ||
89 | int counter; | ||
90 | int i; | ||
91 | |||
92 | outb(0x28, io_port); | ||
93 | |||
94 | counter=0; | ||
95 | while((inb(data_port)&0x01)==0) | ||
96 | if(counter++==256) | ||
97 | break; | ||
98 | |||
99 | while((inb(data_port)&0x01)!=0) | ||
100 | if(counter++==256) | ||
101 | break; | ||
102 | |||
103 | outb(slave, io_port); | ||
104 | |||
105 | counter=0; | ||
106 | while((inb(data_port)&0x01)==0) | ||
107 | if(counter++==256) | ||
108 | break; | ||
109 | |||
110 | while((inb(data_port)&0x01)!=0) | ||
111 | if(counter++==256) | ||
112 | break; | ||
113 | |||
114 | for(i=0;i<12;i++) | ||
115 | { | ||
116 | char st=inb(data_port); | ||
117 | if((st&2)!=0) | ||
118 | return -1; | ||
119 | if((st&1)==0) | ||
120 | break; | ||
121 | } | ||
122 | outb(0x29, io_port); | ||
123 | return inb(data_port); | ||
124 | } | ||
125 | |||
126 | static int pms_i2c_write(u16 slave, u16 sub, u16 data) | ||
127 | { | ||
128 | int skip=0; | ||
129 | int count; | ||
130 | int i; | ||
131 | |||
132 | for(i=0;i<i2c_count;i++) | ||
133 | { | ||
134 | if((i2cinfo[i].slave==slave) && | ||
135 | (i2cinfo[i].sub == sub)) | ||
136 | { | ||
137 | if(i2cinfo[i].data==data) | ||
138 | skip=1; | ||
139 | i2cinfo[i].data=data; | ||
140 | i=i2c_count+1; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | if(i==i2c_count && i2c_count<64) | ||
145 | { | ||
146 | i2cinfo[i2c_count].slave=slave; | ||
147 | i2cinfo[i2c_count].sub=sub; | ||
148 | i2cinfo[i2c_count].data=data; | ||
149 | i2c_count++; | ||
150 | } | ||
151 | |||
152 | if(skip) | ||
153 | return 0; | ||
154 | |||
155 | mvv_write(0x29, sub); | ||
156 | mvv_write(0x2A, data); | ||
157 | mvv_write(0x28, slave); | ||
158 | |||
159 | outb(0x28, io_port); | ||
160 | |||
161 | count=0; | ||
162 | while((inb(data_port)&1)==0) | ||
163 | if(count>255) | ||
164 | break; | ||
165 | while((inb(data_port)&1)!=0) | ||
166 | if(count>255) | ||
167 | break; | ||
168 | |||
169 | count=inb(data_port); | ||
170 | |||
171 | if(count&2) | ||
172 | return -1; | ||
173 | return count; | ||
174 | } | ||
175 | |||
176 | static int pms_i2c_read(int slave, int sub) | ||
177 | { | ||
178 | int i=0; | ||
179 | for(i=0;i<i2c_count;i++) | ||
180 | { | ||
181 | if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub) | ||
182 | return i2cinfo[i].data; | ||
183 | } | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | |||
188 | static void pms_i2c_andor(int slave, int sub, int and, int or) | ||
189 | { | ||
190 | u8 tmp; | ||
191 | |||
192 | tmp=pms_i2c_read(slave, sub); | ||
193 | tmp = (tmp&and)|or; | ||
194 | pms_i2c_write(slave, sub, tmp); | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * Control functions | ||
199 | */ | ||
200 | |||
201 | |||
202 | static void pms_videosource(short source) | ||
203 | { | ||
204 | mvv_write(0x2E, source?0x31:0x30); | ||
205 | } | ||
206 | |||
207 | static void pms_hue(short hue) | ||
208 | { | ||
209 | switch(decoder) | ||
210 | { | ||
211 | case MOTOROLA: | ||
212 | pms_i2c_write(0x8A, 0x00, hue); | ||
213 | break; | ||
214 | case PHILIPS2: | ||
215 | pms_i2c_write(0x8A, 0x07, hue); | ||
216 | break; | ||
217 | case PHILIPS1: | ||
218 | pms_i2c_write(0x42, 0x07, hue); | ||
219 | break; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | static void pms_colour(short colour) | ||
224 | { | ||
225 | switch(decoder) | ||
226 | { | ||
227 | case MOTOROLA: | ||
228 | pms_i2c_write(0x8A, 0x00, colour); | ||
229 | break; | ||
230 | case PHILIPS1: | ||
231 | pms_i2c_write(0x42, 0x12, colour); | ||
232 | break; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | |||
237 | static void pms_contrast(short contrast) | ||
238 | { | ||
239 | switch(decoder) | ||
240 | { | ||
241 | case MOTOROLA: | ||
242 | pms_i2c_write(0x8A, 0x00, contrast); | ||
243 | break; | ||
244 | case PHILIPS1: | ||
245 | pms_i2c_write(0x42, 0x13, contrast); | ||
246 | break; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | static void pms_brightness(short brightness) | ||
251 | { | ||
252 | switch(decoder) | ||
253 | { | ||
254 | case MOTOROLA: | ||
255 | pms_i2c_write(0x8A, 0x00, brightness); | ||
256 | pms_i2c_write(0x8A, 0x00, brightness); | ||
257 | pms_i2c_write(0x8A, 0x00, brightness); | ||
258 | break; | ||
259 | case PHILIPS1: | ||
260 | pms_i2c_write(0x42, 0x19, brightness); | ||
261 | break; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | |||
266 | static void pms_format(short format) | ||
267 | { | ||
268 | int target; | ||
269 | standard = format; | ||
270 | |||
271 | if(decoder==PHILIPS1) | ||
272 | target=0x42; | ||
273 | else if(decoder==PHILIPS2) | ||
274 | target=0x8A; | ||
275 | else | ||
276 | return; | ||
277 | |||
278 | switch(format) | ||
279 | { | ||
280 | case 0: /* Auto */ | ||
281 | pms_i2c_andor(target, 0x0D, 0xFE,0x00); | ||
282 | pms_i2c_andor(target, 0x0F, 0x3F,0x80); | ||
283 | break; | ||
284 | case 1: /* NTSC */ | ||
285 | pms_i2c_andor(target, 0x0D, 0xFE, 0x00); | ||
286 | pms_i2c_andor(target, 0x0F, 0x3F, 0x40); | ||
287 | break; | ||
288 | case 2: /* PAL */ | ||
289 | pms_i2c_andor(target, 0x0D, 0xFE, 0x00); | ||
290 | pms_i2c_andor(target, 0x0F, 0x3F, 0x00); | ||
291 | break; | ||
292 | case 3: /* SECAM */ | ||
293 | pms_i2c_andor(target, 0x0D, 0xFE, 0x01); | ||
294 | pms_i2c_andor(target, 0x0F, 0x3F, 0x00); | ||
295 | break; | ||
296 | } | ||
297 | } | ||
298 | |||
299 | #ifdef FOR_FUTURE_EXPANSION | ||
300 | |||
301 | /* | ||
302 | * These features of the PMS card are not currently exposes. They | ||
303 | * could become a private v4l ioctl for PMSCONFIG or somesuch if | ||
304 | * people need it. We also don't yet use the PMS interrupt. | ||
305 | */ | ||
306 | |||
307 | static void pms_hstart(short start) | ||
308 | { | ||
309 | switch(decoder) | ||
310 | { | ||
311 | case PHILIPS1: | ||
312 | pms_i2c_write(0x8A, 0x05, start); | ||
313 | pms_i2c_write(0x8A, 0x18, start); | ||
314 | break; | ||
315 | case PHILIPS2: | ||
316 | pms_i2c_write(0x42, 0x05, start); | ||
317 | pms_i2c_write(0x42, 0x18, start); | ||
318 | break; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * Bandpass filters | ||
324 | */ | ||
325 | |||
326 | static void pms_bandpass(short pass) | ||
327 | { | ||
328 | if(decoder==PHILIPS2) | ||
329 | pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4); | ||
330 | else if(decoder==PHILIPS1) | ||
331 | pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4); | ||
332 | } | ||
333 | |||
334 | static void pms_antisnow(short snow) | ||
335 | { | ||
336 | if(decoder==PHILIPS2) | ||
337 | pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2); | ||
338 | else if(decoder==PHILIPS1) | ||
339 | pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2); | ||
340 | } | ||
341 | |||
342 | static void pms_sharpness(short sharp) | ||
343 | { | ||
344 | if(decoder==PHILIPS2) | ||
345 | pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03); | ||
346 | else if(decoder==PHILIPS1) | ||
347 | pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03); | ||
348 | } | ||
349 | |||
350 | static void pms_chromaagc(short agc) | ||
351 | { | ||
352 | if(decoder==PHILIPS2) | ||
353 | pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5); | ||
354 | else if(decoder==PHILIPS1) | ||
355 | pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5); | ||
356 | } | ||
357 | |||
358 | static void pms_vertnoise(short noise) | ||
359 | { | ||
360 | if(decoder==PHILIPS2) | ||
361 | pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3); | ||
362 | else if(decoder==PHILIPS1) | ||
363 | pms_i2c_andor(0x42, 0x10, 0xFC, noise&3); | ||
364 | } | ||
365 | |||
366 | static void pms_forcecolour(short colour) | ||
367 | { | ||
368 | if(decoder==PHILIPS2) | ||
369 | pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7); | ||
370 | else if(decoder==PHILIPS1) | ||
371 | pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7); | ||
372 | } | ||
373 | |||
374 | static void pms_antigamma(short gamma) | ||
375 | { | ||
376 | if(decoder==PHILIPS2) | ||
377 | pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7); | ||
378 | else if(decoder==PHILIPS1) | ||
379 | pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7); | ||
380 | } | ||
381 | |||
382 | static void pms_prefilter(short filter) | ||
383 | { | ||
384 | if(decoder==PHILIPS2) | ||
385 | pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6); | ||
386 | else if(decoder==PHILIPS1) | ||
387 | pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6); | ||
388 | } | ||
389 | |||
390 | static void pms_hfilter(short filter) | ||
391 | { | ||
392 | if(decoder==PHILIPS2) | ||
393 | pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5); | ||
394 | else if(decoder==PHILIPS1) | ||
395 | pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5); | ||
396 | } | ||
397 | |||
398 | static void pms_vfilter(short filter) | ||
399 | { | ||
400 | if(decoder==PHILIPS2) | ||
401 | pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5); | ||
402 | else if(decoder==PHILIPS1) | ||
403 | pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5); | ||
404 | } | ||
405 | |||
406 | static void pms_killcolour(short colour) | ||
407 | { | ||
408 | if(decoder==PHILIPS2) | ||
409 | { | ||
410 | pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3); | ||
411 | pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3); | ||
412 | } | ||
413 | else if(decoder==PHILIPS1) | ||
414 | { | ||
415 | pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3); | ||
416 | pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3); | ||
417 | } | ||
418 | } | ||
419 | |||
420 | static void pms_chromagain(short chroma) | ||
421 | { | ||
422 | if(decoder==PHILIPS2) | ||
423 | { | ||
424 | pms_i2c_write(0x8A, 0x11, chroma); | ||
425 | } | ||
426 | else if(decoder==PHILIPS1) | ||
427 | { | ||
428 | pms_i2c_write(0x42, 0x11, chroma); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | |||
433 | static void pms_spacialcompl(short data) | ||
434 | { | ||
435 | mvv_write(0x3B, data); | ||
436 | } | ||
437 | |||
438 | static void pms_spacialcomph(short data) | ||
439 | { | ||
440 | mvv_write(0x3A, data); | ||
441 | } | ||
442 | |||
443 | static void pms_vstart(short start) | ||
444 | { | ||
445 | mvv_write(0x16, start); | ||
446 | mvv_write(0x17, (start>>8)&0x01); | ||
447 | } | ||
448 | |||
449 | #endif | ||
450 | |||
451 | static void pms_secamcross(short cross) | ||
452 | { | ||
453 | if(decoder==PHILIPS2) | ||
454 | pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5); | ||
455 | else if(decoder==PHILIPS1) | ||
456 | pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5); | ||
457 | } | ||
458 | |||
459 | |||
460 | static void pms_swsense(short sense) | ||
461 | { | ||
462 | if(decoder==PHILIPS2) | ||
463 | { | ||
464 | pms_i2c_write(0x8A, 0x0A, sense); | ||
465 | pms_i2c_write(0x8A, 0x0B, sense); | ||
466 | } | ||
467 | else if(decoder==PHILIPS1) | ||
468 | { | ||
469 | pms_i2c_write(0x42, 0x0A, sense); | ||
470 | pms_i2c_write(0x42, 0x0B, sense); | ||
471 | } | ||
472 | } | ||
473 | |||
474 | |||
475 | static void pms_framerate(short frr) | ||
476 | { | ||
477 | int fps=(standard==1)?30:25; | ||
478 | if(frr==0) | ||
479 | return; | ||
480 | fps=fps/frr; | ||
481 | mvv_write(0x14,0x80|fps); | ||
482 | mvv_write(0x15,1); | ||
483 | } | ||
484 | |||
485 | static void pms_vert(u8 deciden, u8 decinum) | ||
486 | { | ||
487 | mvv_write(0x1C, deciden); /* Denominator */ | ||
488 | mvv_write(0x1D, decinum); /* Numerator */ | ||
489 | } | ||
490 | |||
491 | /* | ||
492 | * Turn 16bit ratios into best small ratio the chipset can grok | ||
493 | */ | ||
494 | |||
495 | static void pms_vertdeci(unsigned short decinum, unsigned short deciden) | ||
496 | { | ||
497 | /* Knock it down by /5 once */ | ||
498 | if(decinum%5==0) | ||
499 | { | ||
500 | deciden/=5; | ||
501 | decinum/=5; | ||
502 | } | ||
503 | /* | ||
504 | * 3's | ||
505 | */ | ||
506 | while(decinum%3==0 && deciden%3==0) | ||
507 | { | ||
508 | deciden/=3; | ||
509 | decinum/=3; | ||
510 | } | ||
511 | /* | ||
512 | * 2's | ||
513 | */ | ||
514 | while(decinum%2==0 && deciden%2==0) | ||
515 | { | ||
516 | decinum/=2; | ||
517 | deciden/=2; | ||
518 | } | ||
519 | /* | ||
520 | * Fudgyify | ||
521 | */ | ||
522 | while(deciden>32) | ||
523 | { | ||
524 | deciden/=2; | ||
525 | decinum=(decinum+1)/2; | ||
526 | } | ||
527 | if(deciden==32) | ||
528 | deciden--; | ||
529 | pms_vert(deciden,decinum); | ||
530 | } | ||
531 | |||
532 | static void pms_horzdeci(short decinum, short deciden) | ||
533 | { | ||
534 | if(decinum<=512) | ||
535 | { | ||
536 | if(decinum%5==0) | ||
537 | { | ||
538 | decinum/=5; | ||
539 | deciden/=5; | ||
540 | } | ||
541 | } | ||
542 | else | ||
543 | { | ||
544 | decinum=512; | ||
545 | deciden=640; /* 768 would be ideal */ | ||
546 | } | ||
547 | |||
548 | while(((decinum|deciden)&1)==0) | ||
549 | { | ||
550 | decinum>>=1; | ||
551 | deciden>>=1; | ||
552 | } | ||
553 | while(deciden>32) | ||
554 | { | ||
555 | deciden>>=1; | ||
556 | decinum=(decinum+1)>>1; | ||
557 | } | ||
558 | if(deciden==32) | ||
559 | deciden--; | ||
560 | |||
561 | mvv_write(0x24, 0x80|deciden); | ||
562 | mvv_write(0x25, decinum); | ||
563 | } | ||
564 | |||
565 | static void pms_resolution(short width, short height) | ||
566 | { | ||
567 | int fg_height; | ||
568 | |||
569 | fg_height=height; | ||
570 | if(fg_height>280) | ||
571 | fg_height=280; | ||
572 | |||
573 | mvv_write(0x18, fg_height); | ||
574 | mvv_write(0x19, fg_height>>8); | ||
575 | |||
576 | if(standard==1) | ||
577 | { | ||
578 | mvv_write(0x1A, 0xFC); | ||
579 | mvv_write(0x1B, 0x00); | ||
580 | if(height>fg_height) | ||
581 | pms_vertdeci(240,240); | ||
582 | else | ||
583 | pms_vertdeci(fg_height,240); | ||
584 | } | ||
585 | else | ||
586 | { | ||
587 | mvv_write(0x1A, 0x1A); | ||
588 | mvv_write(0x1B, 0x01); | ||
589 | if(fg_height>256) | ||
590 | pms_vertdeci(270,270); | ||
591 | else | ||
592 | pms_vertdeci(fg_height, 270); | ||
593 | } | ||
594 | mvv_write(0x12,0); | ||
595 | mvv_write(0x13, MVVMEMORYWIDTH); | ||
596 | mvv_write(0x42, 0x00); | ||
597 | mvv_write(0x43, 0x00); | ||
598 | mvv_write(0x44, MVVMEMORYWIDTH); | ||
599 | |||
600 | mvv_write(0x22, width+8); | ||
601 | mvv_write(0x23, (width+8)>> 8); | ||
602 | |||
603 | if(standard==1) | ||
604 | pms_horzdeci(width,640); | ||
605 | else | ||
606 | pms_horzdeci(width+8, 768); | ||
607 | |||
608 | mvv_write(0x30, mvv_read(0x30)&0xFE); | ||
609 | mvv_write(0x08, mvv_read(0x08)|0x01); | ||
610 | mvv_write(0x01, mvv_read(0x01)&0xFD); | ||
611 | mvv_write(0x32, 0x00); | ||
612 | mvv_write(0x33, MVVMEMORYWIDTH); | ||
613 | } | ||
614 | |||
615 | |||
616 | /* | ||
617 | * Set Input | ||
618 | */ | ||
619 | |||
620 | static void pms_vcrinput(short input) | ||
621 | { | ||
622 | if(decoder==PHILIPS2) | ||
623 | pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7); | ||
624 | else if(decoder==PHILIPS1) | ||
625 | pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7); | ||
626 | } | ||
627 | |||
628 | |||
629 | static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count) | ||
630 | { | ||
631 | int y; | ||
632 | int dw = 2*dev->width; | ||
633 | |||
634 | char tmp[dw+32]; /* using a temp buffer is faster than direct */ | ||
635 | int cnt = 0; | ||
636 | int len=0; | ||
637 | unsigned char r8 = 0x5; /* value for reg8 */ | ||
638 | |||
639 | if (rgb555) | ||
640 | r8 |= 0x20; /* else use untranslated rgb = 565 */ | ||
641 | mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */ | ||
642 | |||
643 | /* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */ | ||
644 | |||
645 | for (y = 0; y < dev->height; y++ ) | ||
646 | { | ||
647 | writeb(0, mem); /* synchronisiert neue Zeile */ | ||
648 | |||
649 | /* | ||
650 | * This is in truth a fifo, be very careful as if you | ||
651 | * forgot this odd things will occur 8) | ||
652 | */ | ||
653 | |||
654 | memcpy_fromio(tmp, mem, dw+32); /* discard 16 word */ | ||
655 | cnt -= dev->height; | ||
656 | while (cnt <= 0) | ||
657 | { | ||
658 | /* | ||
659 | * Don't copy too far | ||
660 | */ | ||
661 | int dt=dw; | ||
662 | if(dt+len>count) | ||
663 | dt=count-len; | ||
664 | cnt += dev->height; | ||
665 | if (copy_to_user(buf, tmp+32, dt)) | ||
666 | return len ? len : -EFAULT; | ||
667 | buf += dt; | ||
668 | len += dt; | ||
669 | } | ||
670 | } | ||
671 | return len; | ||
672 | } | ||
673 | |||
674 | |||
675 | /* | ||
676 | * Video4linux interfacing | ||
677 | */ | ||
678 | |||
679 | static int pms_do_ioctl(struct inode *inode, struct file *file, | ||
680 | unsigned int cmd, void *arg) | ||
681 | { | ||
682 | struct video_device *dev = video_devdata(file); | ||
683 | struct pms_device *pd=(struct pms_device *)dev; | ||
684 | |||
685 | switch(cmd) | ||
686 | { | ||
687 | case VIDIOCGCAP: | ||
688 | { | ||
689 | struct video_capability *b = arg; | ||
690 | strcpy(b->name, "Mediavision PMS"); | ||
691 | b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; | ||
692 | b->channels = 4; | ||
693 | b->audios = 0; | ||
694 | b->maxwidth = 640; | ||
695 | b->maxheight = 480; | ||
696 | b->minwidth = 16; | ||
697 | b->minheight = 16; | ||
698 | return 0; | ||
699 | } | ||
700 | case VIDIOCGCHAN: | ||
701 | { | ||
702 | struct video_channel *v = arg; | ||
703 | if(v->channel<0 || v->channel>3) | ||
704 | return -EINVAL; | ||
705 | v->flags=0; | ||
706 | v->tuners=1; | ||
707 | /* Good question.. its composite or SVHS so.. */ | ||
708 | v->type = VIDEO_TYPE_CAMERA; | ||
709 | switch(v->channel) | ||
710 | { | ||
711 | case 0: | ||
712 | strcpy(v->name, "Composite");break; | ||
713 | case 1: | ||
714 | strcpy(v->name, "SVideo");break; | ||
715 | case 2: | ||
716 | strcpy(v->name, "Composite(VCR)");break; | ||
717 | case 3: | ||
718 | strcpy(v->name, "SVideo(VCR)");break; | ||
719 | } | ||
720 | return 0; | ||
721 | } | ||
722 | case VIDIOCSCHAN: | ||
723 | { | ||
724 | struct video_channel *v = arg; | ||
725 | if(v->channel<0 || v->channel>3) | ||
726 | return -EINVAL; | ||
727 | down(&pd->lock); | ||
728 | pms_videosource(v->channel&1); | ||
729 | pms_vcrinput(v->channel>>1); | ||
730 | up(&pd->lock); | ||
731 | return 0; | ||
732 | } | ||
733 | case VIDIOCGTUNER: | ||
734 | { | ||
735 | struct video_tuner *v = arg; | ||
736 | if(v->tuner) | ||
737 | return -EINVAL; | ||
738 | strcpy(v->name, "Format"); | ||
739 | v->rangelow=0; | ||
740 | v->rangehigh=0; | ||
741 | v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; | ||
742 | switch(standard) | ||
743 | { | ||
744 | case 0: | ||
745 | v->mode = VIDEO_MODE_AUTO; | ||
746 | break; | ||
747 | case 1: | ||
748 | v->mode = VIDEO_MODE_NTSC; | ||
749 | break; | ||
750 | case 2: | ||
751 | v->mode = VIDEO_MODE_PAL; | ||
752 | break; | ||
753 | case 3: | ||
754 | v->mode = VIDEO_MODE_SECAM; | ||
755 | break; | ||
756 | } | ||
757 | return 0; | ||
758 | } | ||
759 | case VIDIOCSTUNER: | ||
760 | { | ||
761 | struct video_tuner *v = arg; | ||
762 | if(v->tuner) | ||
763 | return -EINVAL; | ||
764 | down(&pd->lock); | ||
765 | switch(v->mode) | ||
766 | { | ||
767 | case VIDEO_MODE_AUTO: | ||
768 | pms_framerate(25); | ||
769 | pms_secamcross(0); | ||
770 | pms_format(0); | ||
771 | break; | ||
772 | case VIDEO_MODE_NTSC: | ||
773 | pms_framerate(30); | ||
774 | pms_secamcross(0); | ||
775 | pms_format(1); | ||
776 | break; | ||
777 | case VIDEO_MODE_PAL: | ||
778 | pms_framerate(25); | ||
779 | pms_secamcross(0); | ||
780 | pms_format(2); | ||
781 | break; | ||
782 | case VIDEO_MODE_SECAM: | ||
783 | pms_framerate(25); | ||
784 | pms_secamcross(1); | ||
785 | pms_format(2); | ||
786 | break; | ||
787 | default: | ||
788 | up(&pd->lock); | ||
789 | return -EINVAL; | ||
790 | } | ||
791 | up(&pd->lock); | ||
792 | return 0; | ||
793 | } | ||
794 | case VIDIOCGPICT: | ||
795 | { | ||
796 | struct video_picture *p = arg; | ||
797 | *p = pd->picture; | ||
798 | return 0; | ||
799 | } | ||
800 | case VIDIOCSPICT: | ||
801 | { | ||
802 | struct video_picture *p = arg; | ||
803 | if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16) | ||
804 | ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15))) | ||
805 | return -EINVAL; | ||
806 | pd->picture= *p; | ||
807 | |||
808 | /* | ||
809 | * Now load the card. | ||
810 | */ | ||
811 | |||
812 | down(&pd->lock); | ||
813 | pms_brightness(p->brightness>>8); | ||
814 | pms_hue(p->hue>>8); | ||
815 | pms_colour(p->colour>>8); | ||
816 | pms_contrast(p->contrast>>8); | ||
817 | up(&pd->lock); | ||
818 | return 0; | ||
819 | } | ||
820 | case VIDIOCSWIN: | ||
821 | { | ||
822 | struct video_window *vw = arg; | ||
823 | if(vw->flags) | ||
824 | return -EINVAL; | ||
825 | if(vw->clipcount) | ||
826 | return -EINVAL; | ||
827 | if(vw->height<16||vw->height>480) | ||
828 | return -EINVAL; | ||
829 | if(vw->width<16||vw->width>640) | ||
830 | return -EINVAL; | ||
831 | pd->width=vw->width; | ||
832 | pd->height=vw->height; | ||
833 | down(&pd->lock); | ||
834 | pms_resolution(pd->width, pd->height); | ||
835 | up(&pd->lock); /* Ok we figured out what to use from our wide choice */ | ||
836 | return 0; | ||
837 | } | ||
838 | case VIDIOCGWIN: | ||
839 | { | ||
840 | struct video_window *vw = arg; | ||
841 | memset(vw,0,sizeof(*vw)); | ||
842 | vw->width=pd->width; | ||
843 | vw->height=pd->height; | ||
844 | return 0; | ||
845 | } | ||
846 | case VIDIOCKEY: | ||
847 | return 0; | ||
848 | case VIDIOCCAPTURE: | ||
849 | case VIDIOCGFBUF: | ||
850 | case VIDIOCSFBUF: | ||
851 | case VIDIOCGFREQ: | ||
852 | case VIDIOCSFREQ: | ||
853 | case VIDIOCGAUDIO: | ||
854 | case VIDIOCSAUDIO: | ||
855 | return -EINVAL; | ||
856 | default: | ||
857 | return -ENOIOCTLCMD; | ||
858 | } | ||
859 | return 0; | ||
860 | } | ||
861 | |||
862 | static int pms_ioctl(struct inode *inode, struct file *file, | ||
863 | unsigned int cmd, unsigned long arg) | ||
864 | { | ||
865 | return video_usercopy(inode, file, cmd, arg, pms_do_ioctl); | ||
866 | } | ||
867 | |||
868 | static ssize_t pms_read(struct file *file, char __user *buf, | ||
869 | size_t count, loff_t *ppos) | ||
870 | { | ||
871 | struct video_device *v = video_devdata(file); | ||
872 | struct pms_device *pd=(struct pms_device *)v; | ||
873 | int len; | ||
874 | |||
875 | down(&pd->lock); | ||
876 | len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count); | ||
877 | up(&pd->lock); | ||
878 | return len; | ||
879 | } | ||
880 | |||
881 | static struct file_operations pms_fops = { | ||
882 | .owner = THIS_MODULE, | ||
883 | .open = video_exclusive_open, | ||
884 | .release = video_exclusive_release, | ||
885 | .ioctl = pms_ioctl, | ||
886 | .read = pms_read, | ||
887 | .llseek = no_llseek, | ||
888 | }; | ||
889 | |||
890 | static struct video_device pms_template= | ||
891 | { | ||
892 | .owner = THIS_MODULE, | ||
893 | .name = "Mediavision PMS", | ||
894 | .type = VID_TYPE_CAPTURE, | ||
895 | .hardware = VID_HARDWARE_PMS, | ||
896 | .fops = &pms_fops, | ||
897 | }; | ||
898 | |||
899 | static struct pms_device pms_device; | ||
900 | |||
901 | |||
902 | /* | ||
903 | * Probe for and initialise the Mediavision PMS | ||
904 | */ | ||
905 | |||
906 | static int init_mediavision(void) | ||
907 | { | ||
908 | int id; | ||
909 | int idec, decst; | ||
910 | int i; | ||
911 | |||
912 | unsigned char i2c_defs[]={ | ||
913 | 0x4C,0x30,0x00,0xE8, | ||
914 | 0xB6,0xE2,0x00,0x00, | ||
915 | 0xFF,0xFF,0x00,0x00, | ||
916 | 0x00,0x00,0x78,0x98, | ||
917 | 0x00,0x00,0x00,0x00, | ||
918 | 0x34,0x0A,0xF4,0xCE, | ||
919 | 0xE4 | ||
920 | }; | ||
921 | |||
922 | mem = ioremap(mem_base, 0x800); | ||
923 | if (!mem) | ||
924 | return -ENOMEM; | ||
925 | |||
926 | if (!request_region(0x9A01, 1, "Mediavision PMS config")) | ||
927 | { | ||
928 | printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n"); | ||
929 | iounmap(mem); | ||
930 | return -EBUSY; | ||
931 | } | ||
932 | if (!request_region(io_port, 3, "Mediavision PMS")) | ||
933 | { | ||
934 | printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port); | ||
935 | release_region(0x9A01, 1); | ||
936 | iounmap(mem); | ||
937 | return -EBUSY; | ||
938 | } | ||
939 | outb(0xB8, 0x9A01); /* Unlock */ | ||
940 | outb(io_port>>4, 0x9A01); /* Set IO port */ | ||
941 | |||
942 | |||
943 | id=mvv_read(3); | ||
944 | decst=pms_i2c_stat(0x43); | ||
945 | |||
946 | if(decst!=-1) | ||
947 | idec=2; | ||
948 | else if(pms_i2c_stat(0xb9)!=-1) | ||
949 | idec=3; | ||
950 | else if(pms_i2c_stat(0x8b)!=-1) | ||
951 | idec=1; | ||
952 | else | ||
953 | idec=0; | ||
954 | |||
955 | printk(KERN_INFO "PMS type is %d\n", idec); | ||
956 | if(idec == 0) { | ||
957 | release_region(io_port, 3); | ||
958 | release_region(0x9A01, 1); | ||
959 | iounmap(mem); | ||
960 | return -ENODEV; | ||
961 | } | ||
962 | |||
963 | /* | ||
964 | * Ok we have a PMS of some sort | ||
965 | */ | ||
966 | |||
967 | mvv_write(0x04, mem_base>>12); /* Set the memory area */ | ||
968 | |||
969 | /* Ok now load the defaults */ | ||
970 | |||
971 | for(i=0;i<0x19;i++) | ||
972 | { | ||
973 | if(i2c_defs[i]==0xFF) | ||
974 | pms_i2c_andor(0x8A, i, 0x07,0x00); | ||
975 | else | ||
976 | pms_i2c_write(0x8A, i, i2c_defs[i]); | ||
977 | } | ||
978 | |||
979 | pms_i2c_write(0xB8,0x00,0x12); | ||
980 | pms_i2c_write(0xB8,0x04,0x00); | ||
981 | pms_i2c_write(0xB8,0x07,0x00); | ||
982 | pms_i2c_write(0xB8,0x08,0x00); | ||
983 | pms_i2c_write(0xB8,0x09,0xFF); | ||
984 | pms_i2c_write(0xB8,0x0A,0x00); | ||
985 | pms_i2c_write(0xB8,0x0B,0x10); | ||
986 | pms_i2c_write(0xB8,0x10,0x03); | ||
987 | |||
988 | mvv_write(0x01, 0x00); | ||
989 | mvv_write(0x05, 0xA0); | ||
990 | mvv_write(0x08, 0x25); | ||
991 | mvv_write(0x09, 0x00); | ||
992 | mvv_write(0x0A, 0x20|MVVMEMORYWIDTH); | ||
993 | |||
994 | mvv_write(0x10, 0x02); | ||
995 | mvv_write(0x1E, 0x0C); | ||
996 | mvv_write(0x1F, 0x03); | ||
997 | mvv_write(0x26, 0x06); | ||
998 | |||
999 | mvv_write(0x2B, 0x00); | ||
1000 | mvv_write(0x2C, 0x20); | ||
1001 | mvv_write(0x2D, 0x00); | ||
1002 | mvv_write(0x2F, 0x70); | ||
1003 | mvv_write(0x32, 0x00); | ||
1004 | mvv_write(0x33, MVVMEMORYWIDTH); | ||
1005 | mvv_write(0x34, 0x00); | ||
1006 | mvv_write(0x35, 0x00); | ||
1007 | mvv_write(0x3A, 0x80); | ||
1008 | mvv_write(0x3B, 0x10); | ||
1009 | mvv_write(0x20, 0x00); | ||
1010 | mvv_write(0x21, 0x00); | ||
1011 | mvv_write(0x30, 0x22); | ||
1012 | return 0; | ||
1013 | } | ||
1014 | |||
1015 | /* | ||
1016 | * Initialization and module stuff | ||
1017 | */ | ||
1018 | |||
1019 | static int __init init_pms_cards(void) | ||
1020 | { | ||
1021 | printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); | ||
1022 | |||
1023 | data_port = io_port +1; | ||
1024 | |||
1025 | if(init_mediavision()) | ||
1026 | { | ||
1027 | printk(KERN_INFO "Board not found.\n"); | ||
1028 | return -ENODEV; | ||
1029 | } | ||
1030 | memcpy(&pms_device, &pms_template, sizeof(pms_template)); | ||
1031 | init_MUTEX(&pms_device.lock); | ||
1032 | pms_device.height=240; | ||
1033 | pms_device.width=320; | ||
1034 | pms_swsense(75); | ||
1035 | pms_resolution(320,240); | ||
1036 | return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr); | ||
1037 | } | ||
1038 | |||
1039 | module_param(io_port, int, 0); | ||
1040 | module_param(mem_base, int, 0); | ||
1041 | module_param(video_nr, int, 0); | ||
1042 | MODULE_LICENSE("GPL"); | ||
1043 | |||
1044 | |||
1045 | static void __exit shutdown_mediavision(void) | ||
1046 | { | ||
1047 | release_region(io_port,3); | ||
1048 | release_region(0x9A01, 1); | ||
1049 | } | ||
1050 | |||
1051 | static void __exit cleanup_pms_module(void) | ||
1052 | { | ||
1053 | shutdown_mediavision(); | ||
1054 | video_unregister_device((struct video_device *)&pms_device); | ||
1055 | iounmap(mem); | ||
1056 | } | ||
1057 | |||
1058 | module_init(init_pms_cards); | ||
1059 | module_exit(cleanup_pms_module); | ||
1060 | |||