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/videodev.h>
32#include <media/v4l2-common.h>
33#include <linux/mutex.h>
34
35#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;
49	struct mutex lock;
50};
51
52struct i2c_info
53{
54	u8 slave;
55	u8 sub;
56	u8 data;
57	u8 hits;
58};
59
60static int i2c_count 		= 0;
61static struct i2c_info i2cinfo[64];
62
63static int decoder 		= PHILIPS2;
64static int standard 		= 0;	/* 0 - auto 1 - ntsc 2 - pal 3 - secam */
65
66/*
67 *	I/O ports and Shared Memory
68 */
69
70static int io_port		=	0x250;
71static int data_port		=	0x251;
72static int mem_base		=	0xC8000;
73static void __iomem *mem;
74static int video_nr             =       -1;
75
76
77
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;
93
94	outb(0x28, io_port);
95
96	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;
104
105	outb(slave, io_port);
106
107	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;
115
116	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);
125	return inb(data_port);
126}
127
128static int pms_i2c_write(u16 slave, u16 sub, u16 data)
129{
130	int skip=0;
131	int count;
132	int i;
133
134	for(i=0;i<i2c_count;i++)
135	{
136		if((i2cinfo[i].slave==slave) &&
137		   (i2cinfo[i].sub == sub))
138		{
139			if(i2cinfo[i].data==data)
140				skip=1;
141			i2cinfo[i].data=data;
142			i=i2c_count+1;
143		}
144	}
145
146	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	}
153
154	if(skip)
155		return 0;
156
157	mvv_write(0x29, sub);
158	mvv_write(0x2A, data);
159	mvv_write(0x28, slave);
160
161	outb(0x28, io_port);
162
163	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;
170
171	count=inb(data_port);
172
173	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{
192	u8 tmp;
193
194	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 */
202
203
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}
237
238
239static 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;
272
273	if(decoder==PHILIPS1)
274		target=0x42;
275	else if(decoder==PHILIPS2)
276		target=0x8A;
277	else
278		return;
279
280	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
305 *	could become a private v4l ioctl for PMSCONFIG or somesuch if
306 *	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 */
327
328static 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 */
496
497static 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	}
549
550	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--;
562
563	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;
570
571	fg_height=height;
572	if(fg_height>280)
573		fg_height=280;
574
575	mvv_write(0x18, fg_height);
576	mvv_write(0x19, fg_height>>8);
577
578	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);
601
602	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 */
621
622static 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); */
646
647	for (y = 0; y < dev->height; y++ )
648	{
649		writeb(0, mem);  /* synchronisiert neue Zeile */
650
651		/*
652		 *	This is in truth a fifo, be very careful as if you
653		 *	forgot this odd things will occur 8)
654		 */
655
656		memcpy_fromio(tmp, mem, dw+32); /* discard 16 word   */
657		cnt -= dev->height;
658		while (cnt <= 0)
659		{
660			/*
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;
669			buf += dt;
670			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;
686
687	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;
729			mutex_lock(&pd->lock);
730			pms_videosource(v->channel&1);
731			pms_vcrinput(v->channel>>1);
732			mutex_unlock(&pd->lock);
733			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;
766			mutex_lock(&pd->lock);
767			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:
790					mutex_unlock(&pd->lock);
791					return -EINVAL;
792			}
793			mutex_unlock(&pd->lock);
794			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)))
807				return -EINVAL;
808			pd->picture= *p;
809
810			/*
811			 *	Now load the card.
812			 */
813
814			mutex_lock(&pd->lock);
815			pms_brightness(p->brightness>>8);
816			pms_hue(p->hue>>8);
817			pms_colour(p->colour>>8);
818			pms_contrast(p->contrast>>8);
819			mutex_unlock(&pd->lock);
820			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;
835			mutex_lock(&pd->lock);
836			pms_resolution(pd->width, pd->height);
837			mutex_unlock(&pd->lock);			/* Ok we figured out what to use from our wide choice */
838			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;
876
877	mutex_lock(&pd->lock);
878	len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count);
879	mutex_unlock(&pd->lock);
880	return len;
881}
882
883static const struct file_operations pms_fops = {
884	.owner		= THIS_MODULE,
885	.open           = video_exclusive_open,
886	.release        = video_exclusive_release,
887	.ioctl          = pms_ioctl,
888	.compat_ioctl	= v4l_compat_ioctl32,
889	.read           = pms_read,
890	.llseek         = no_llseek,
891};
892
893static struct video_device pms_template=
894{
895	.owner		= THIS_MODULE,
896	.name		= "Mediavision PMS",
897	.type		= VID_TYPE_CAPTURE,
898	.hardware	= VID_HARDWARE_PMS,
899	.fops           = &pms_fops,
900};
901
902static struct pms_device pms_device;
903
904
905/*
906 *	Probe for and initialise the Mediavision PMS
907 */
908
909static int init_mediavision(void)
910{
911	int id;
912	int idec, decst;
913	int i;
914
915	unsigned char i2c_defs[]={
916		0x4C,0x30,0x00,0xE8,
917		0xB6,0xE2,0x00,0x00,
918		0xFF,0xFF,0x00,0x00,
919		0x00,0x00,0x78,0x98,
920		0x00,0x00,0x00,0x00,
921		0x34,0x0A,0xF4,0xCE,
922		0xE4
923	};
924
925	mem = ioremap(mem_base, 0x800);
926	if (!mem)
927		return -ENOMEM;
928
929	if (!request_region(0x9A01, 1, "Mediavision PMS config"))
930	{
931		printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n");
932		iounmap(mem);
933		return -EBUSY;
934	}
935	if (!request_region(io_port, 3, "Mediavision PMS"))
936	{
937		printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port);
938		release_region(0x9A01, 1);
939		iounmap(mem);
940		return -EBUSY;
941	}
942	outb(0xB8, 0x9A01);		/* Unlock */
943	outb(io_port>>4, 0x9A01);	/* Set IO port */
944
945
946	id=mvv_read(3);
947	decst=pms_i2c_stat(0x43);
948
949	if(decst!=-1)
950		idec=2;
951	else if(pms_i2c_stat(0xb9)!=-1)
952		idec=3;
953	else if(pms_i2c_stat(0x8b)!=-1)
954		idec=1;
955	else
956		idec=0;
957
958	printk(KERN_INFO "PMS type is %d\n", idec);
959	if(idec == 0) {
960		release_region(io_port, 3);
961		release_region(0x9A01, 1);
962		iounmap(mem);
963		return -ENODEV;
964	}
965
966	/*
967	 *	Ok we have a PMS of some sort
968	 */
969
970	mvv_write(0x04, mem_base>>12);	/* Set the memory area */
971
972	/* Ok now load the defaults */
973
974	for(i=0;i<0x19;i++)
975	{
976		if(i2c_defs[i]==0xFF)
977			pms_i2c_andor(0x8A, i, 0x07,0x00);
978		else
979			pms_i2c_write(0x8A, i, i2c_defs[i]);
980	}
981
982	pms_i2c_write(0xB8,0x00,0x12);
983	pms_i2c_write(0xB8,0x04,0x00);
984	pms_i2c_write(0xB8,0x07,0x00);
985	pms_i2c_write(0xB8,0x08,0x00);
986	pms_i2c_write(0xB8,0x09,0xFF);
987	pms_i2c_write(0xB8,0x0A,0x00);
988	pms_i2c_write(0xB8,0x0B,0x10);
989	pms_i2c_write(0xB8,0x10,0x03);
990
991	mvv_write(0x01, 0x00);
992	mvv_write(0x05, 0xA0);
993	mvv_write(0x08, 0x25);
994	mvv_write(0x09, 0x00);
995	mvv_write(0x0A, 0x20|MVVMEMORYWIDTH);
996
997	mvv_write(0x10, 0x02);
998	mvv_write(0x1E, 0x0C);
999	mvv_write(0x1F, 0x03);
1000	mvv_write(0x26, 0x06);
1001
1002	mvv_write(0x2B, 0x00);
1003	mvv_write(0x2C, 0x20);
1004	mvv_write(0x2D, 0x00);
1005	mvv_write(0x2F, 0x70);
1006	mvv_write(0x32, 0x00);
1007	mvv_write(0x33, MVVMEMORYWIDTH);
1008	mvv_write(0x34, 0x00);
1009	mvv_write(0x35, 0x00);
1010	mvv_write(0x3A, 0x80);
1011	mvv_write(0x3B, 0x10);
1012	mvv_write(0x20, 0x00);
1013	mvv_write(0x21, 0x00);
1014	mvv_write(0x30, 0x22);
1015	return 0;
1016}
1017
1018/*
1019 *	Initialization and module stuff
1020 */
1021
1022static int __init init_pms_cards(void)
1023{
1024	printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n");
1025
1026	data_port = io_port +1;
1027
1028	if(init_mediavision())
1029	{
1030		printk(KERN_INFO "Board not found.\n");
1031		return -ENODEV;
1032	}
1033	memcpy(&pms_device, &pms_template, sizeof(pms_template));
1034	mutex_init(&pms_device.lock);
1035	pms_device.height=240;
1036	pms_device.width=320;
1037	pms_swsense(75);
1038	pms_resolution(320,240);
1039	return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr);
1040}
1041
1042module_param(io_port, int, 0);
1043module_param(mem_base, int, 0);
1044module_param(video_nr, int, 0);
1045MODULE_LICENSE("GPL");
1046
1047
1048static void __exit shutdown_mediavision(void)
1049{
1050	release_region(io_port,3);
1051	release_region(0x9A01, 1);
1052}
1053
1054static void __exit cleanup_pms_module(void)
1055{
1056	shutdown_mediavision();
1057	video_unregister_device((struct video_device *)&pms_device);
1058	iounmap(mem);
1059}
1060
1061module_init(init_pms_cards);
1062module_exit(cleanup_pms_module);
1063