blob: 952b0b9865e17614ae9cc612d6dcb4967eba2d98 [file] [log] [blame]
/*
* Media Vision Pro Movie Studio
* or
* "all you need is an I2C bus some RAM and a prayer"
*
* This draws heavily on code
*
* (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994
* Kiefernring 15
* 14478 Potsdam, Germany
*
* Most of this code is directly derived from his userspace driver.
* His driver works so send any reports to alan@redhat.com unless the
* userspace driver also doesn't work for you...
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/sched.h>
#include <linux/videodev.h>
#include <linux/version.h>
#include <asm/uaccess.h>
#define MOTOROLA 1
#define PHILIPS2 2
#define PHILIPS1 3
#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
struct pms_device
{
struct video_device v;
struct video_picture picture;
int height;
int width;
struct semaphore lock;
};
struct i2c_info
{
u8 slave;
u8 sub;
u8 data;
u8 hits;
};
static int i2c_count = 0;
static struct i2c_info i2cinfo[64];
static int decoder = PHILIPS2;
static int standard = 0; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
/*
* I/O ports and Shared Memory
*/
static int io_port = 0x250;
static int data_port = 0x251;
static int mem_base = 0xC8000;
static int video_nr = -1;
static inline void mvv_write(u8 index, u8 value)
{
outw(index|(value<<8), io_port);
}
static inline u8 mvv_read(u8 index)
{
outb(index, io_port);
return inb(data_port);
}
static int pms_i2c_stat(u8 slave)
{
int counter;
int i;
outb(0x28, io_port);
counter=0;
while((inb(data_port)&0x01)==0)
if(counter++==256)
break;
while((inb(data_port)&0x01)!=0)
if(counter++==256)
break;
outb(slave, io_port);
counter=0;
while((inb(data_port)&0x01)==0)
if(counter++==256)
break;
while((inb(data_port)&0x01)!=0)
if(counter++==256)
break;
for(i=0;i<12;i++)
{
char st=inb(data_port);
if((st&2)!=0)
return -1;
if((st&1)==0)
break;
}
outb(0x29, io_port);
return inb(data_port);
}
static int pms_i2c_write(u16 slave, u16 sub, u16 data)
{
int skip=0;
int count;
int i;
for(i=0;i<i2c_count;i++)
{
if((i2cinfo[i].slave==slave) &&
(i2cinfo[i].sub == sub))
{
if(i2cinfo[i].data==data)
skip=1;
i2cinfo[i].data=data;
i=i2c_count+1;
}
}
if(i==i2c_count && i2c_count<64)
{
i2cinfo[i2c_count].slave=slave;
i2cinfo[i2c_count].sub=sub;
i2cinfo[i2c_count].data=data;
i2c_count++;
}
if(skip)
return 0;
mvv_write(0x29, sub);
mvv_write(0x2A, data);
mvv_write(0x28, slave);
outb(0x28, io_port);
count=0;
while((inb(data_port)&1)==0)
if(count>255)
break;
while((inb(data_port)&1)!=0)
if(count>255)
break;
count=inb(data_port);
if(count&2)
return -1;
return count;
}
static int pms_i2c_read(int slave, int sub)
{
int i=0;
for(i=0;i<i2c_count;i++)
{
if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub)
return i2cinfo[i].data;
}
return 0;
}
static void pms_i2c_andor(int slave, int sub, int and, int or)
{
u8 tmp;
tmp=pms_i2c_read(slave, sub);
tmp = (tmp&and)|or;
pms_i2c_write(slave, sub, tmp);
}
/*
* Control functions
*/
static void pms_videosource(short source)
{
mvv_write(0x2E, source?0x31:0x30);
}
static void pms_hue(short hue)
{
switch(decoder)
{
case MOTOROLA:
pms_i2c_write(0x8A, 0x00, hue);
break;
case PHILIPS2:
pms_i2c_write(0x8A, 0x07, hue);
break;
case PHILIPS1:
pms_i2c_write(0x42, 0x07, hue);
break;
}
}
static void pms_colour(short colour)
{
switch(decoder)
{
case MOTOROLA:
pms_i2c_write(0x8A, 0x00, colour);
break;
case PHILIPS1:
pms_i2c_write(0x42, 0x12, colour);
break;
}
}
static void pms_contrast(short contrast)
{
switch(decoder)
{
case MOTOROLA:
pms_i2c_write(0x8A, 0x00, contrast);
break;
case PHILIPS1:
pms_i2c_write(0x42, 0x13, contrast);
break;
}
}
static void pms_brightness(short brightness)
{
switch(decoder)
{
case MOTOROLA:
pms_i2c_write(0x8A, 0x00, brightness);
pms_i2c_write(0x8A, 0x00, brightness);
pms_i2c_write(0x8A, 0x00, brightness);
break;
case PHILIPS1:
pms_i2c_write(0x42, 0x19, brightness);
break;
}
}
static void pms_format(short format)
{
int target;
standard = format;
if(decoder==PHILIPS1)
target=0x42;
else if(decoder==PHILIPS2)
target=0x8A;
else
return;
switch(format)
{
case 0: /* Auto */
pms_i2c_andor(target, 0x0D, 0xFE,0x00);
pms_i2c_andor(target, 0x0F, 0x3F,0x80);
break;
case 1: /* NTSC */
pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
pms_i2c_andor(target, 0x0F, 0x3F, 0x40);
break;
case 2: /* PAL */
pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
break;
case 3: /* SECAM */
pms_i2c_andor(target, 0x0D, 0xFE, 0x01);
pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
break;
}
}
#ifdef FOR_FUTURE_EXPANSION
/*
* These features of the PMS card are not currently exposes. They
* could become a private v4l ioctl for PMSCONFIG or somesuch if
* people need it. We also don't yet use the PMS interrupt.
*/
static void pms_hstart(short start)
{
switch(decoder)
{
case PHILIPS1:
pms_i2c_write(0x8A, 0x05, start);
pms_i2c_write(0x8A, 0x18, start);
break;
case PHILIPS2:
pms_i2c_write(0x42, 0x05, start);
pms_i2c_write(0x42, 0x18, start);
break;
}
}
/*
* Bandpass filters
*/
static void pms_bandpass(short pass)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4);
}
static void pms_antisnow(short snow)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2);
}
static void pms_sharpness(short sharp)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03);
}
static void pms_chromaagc(short agc)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5);
}
static void pms_vertnoise(short noise)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x10, 0xFC, noise&3);
}
static void pms_forcecolour(short colour)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7);
}
static void pms_antigamma(short gamma)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7);
}
static void pms_prefilter(short filter)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6);
}
static void pms_hfilter(short filter)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5);
}
static void pms_vfilter(short filter)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5);
}
static void pms_killcolour(short colour)
{
if(decoder==PHILIPS2)
{
pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3);
pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3);
}
else if(decoder==PHILIPS1)
{
pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3);
pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3);
}
}
static void pms_chromagain(short chroma)
{
if(decoder==PHILIPS2)
{
pms_i2c_write(0x8A, 0x11, chroma);
}
else if(decoder==PHILIPS1)
{
pms_i2c_write(0x42, 0x11, chroma);
}
}
static void pms_spacialcompl(short data)
{
mvv_write(0x3B, data);
}
static void pms_spacialcomph(short data)
{
mvv_write(0x3A, data);
}
static void pms_vstart(short start)
{
mvv_write(0x16, start);
mvv_write(0x17, (start>>8)&0x01);
}
#endif
static void pms_secamcross(short cross)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
}
static void pms_swsense(short sense)
{
if(decoder==PHILIPS2)
{
pms_i2c_write(0x8A, 0x0A, sense);
pms_i2c_write(0x8A, 0x0B, sense);
}
else if(decoder==PHILIPS1)
{
pms_i2c_write(0x42, 0x0A, sense);
pms_i2c_write(0x42, 0x0B, sense);
}
}
static void pms_framerate(short frr)
{
int fps=(standard==1)?30:25;
if(frr==0)
return;
fps=fps/frr;
mvv_write(0x14,0x80|fps);
mvv_write(0x15,1);
}
static void pms_vert(u8 deciden, u8 decinum)
{
mvv_write(0x1C, deciden); /* Denominator */
mvv_write(0x1D, decinum); /* Numerator */
}
/*
* Turn 16bit ratios into best small ratio the chipset can grok
*/
static void pms_vertdeci(unsigned short decinum, unsigned short deciden)
{
/* Knock it down by /5 once */
if(decinum%5==0)
{
deciden/=5;
decinum/=5;
}
/*
* 3's
*/
while(decinum%3==0 && deciden%3==0)
{
deciden/=3;
decinum/=3;
}
/*
* 2's
*/
while(decinum%2==0 && deciden%2==0)
{
decinum/=2;
deciden/=2;
}
/*
* Fudgyify
*/
while(deciden>32)
{
deciden/=2;
decinum=(decinum+1)/2;
}
if(deciden==32)
deciden--;
pms_vert(deciden,decinum);
}
static void pms_horzdeci(short decinum, short deciden)
{
if(decinum<=512)
{
if(decinum%5==0)
{
decinum/=5;
deciden/=5;
}
}
else
{
decinum=512;
deciden=640; /* 768 would be ideal */
}
while(((decinum|deciden)&1)==0)
{
decinum>>=1;
deciden>>=1;
}
while(deciden>32)
{
deciden>>=1;
decinum=(decinum+1)>>1;
}
if(deciden==32)
deciden--;
mvv_write(0x24, 0x80|deciden);
mvv_write(0x25, decinum);
}
static void pms_resolution(short width, short height)
{
int fg_height;
fg_height=height;
if(fg_height>280)
fg_height=280;
mvv_write(0x18, fg_height);
mvv_write(0x19, fg_height>>8);
if(standard==1)
{
mvv_write(0x1A, 0xFC);
mvv_write(0x1B, 0x00);
if(height>fg_height)
pms_vertdeci(240,240);
else
pms_vertdeci(fg_height,240);
}
else
{
mvv_write(0x1A, 0x1A);
mvv_write(0x1B, 0x01);
if(fg_height>256)
pms_vertdeci(270,270);
else
pms_vertdeci(fg_height, 270);
}
mvv_write(0x12,0);
mvv_write(0x13, MVVMEMORYWIDTH);
mvv_write(0x42, 0x00);
mvv_write(0x43, 0x00);
mvv_write(0x44, MVVMEMORYWIDTH);
mvv_write(0x22, width+8);
mvv_write(0x23, (width+8)>> 8);
if(standard==1)
pms_horzdeci(width,640);
else
pms_horzdeci(width+8, 768);
mvv_write(0x30, mvv_read(0x30)&0xFE);
mvv_write(0x08, mvv_read(0x08)|0x01);
mvv_write(0x01, mvv_read(0x01)&0xFD);
mvv_write(0x32, 0x00);
mvv_write(0x33, MVVMEMORYWIDTH);
}
/*
* Set Input
*/
static void pms_vcrinput(short input)
{
if(decoder==PHILIPS2)
pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7);
else if(decoder==PHILIPS1)
pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7);
}
static int pms_capture(struct pms_device *dev, char *buf, int rgb555, int count)
{
int y;
int dw = 2*dev->width;
u32 src = mem_base;
char tmp[dw+32]; /* using a temp buffer is faster than direct */
int cnt = 0;
int len=0;
unsigned char r8 = 0x5; /* value for reg8 */
if (rgb555)
r8 |= 0x20; /* else use untranslated rgb = 565 */
mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */
/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
for (y = 0; y < dev->height; y++ )
{
isa_writeb(0, src); /* synchronisiert neue Zeile */
/*
* This is in truth a fifo, be very careful as if you
* forgot this odd things will occur 8)
*/
isa_memcpy_fromio(tmp, src, dw+32); /* discard 16 word */
cnt -= dev->height;
while (cnt <= 0)
{
/*
* Don't copy too far
*/
int dt=dw;
if(dt+len>count)
dt=count-len;
cnt += dev->height;
copy_to_user(buf, tmp+32, dt);
buf += dt;
len += dt;
}
}
return len;
}
/*
* Video4linux interfacing
*/
static int pms_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
struct pms_device *pd=(struct pms_device *)dev;
switch(cmd)
{
case VIDIOCGCAP:
{
struct video_capability *b = arg;
strcpy(b->name, "Mediavision PMS");
b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
b->channels = 4;
b->audios = 0;
b->maxwidth = 640;
b->maxheight = 480;
b->minwidth = 16;
b->minheight = 16;
return 0;
}
case VIDIOCGCHAN:
{
struct video_channel *v = arg;
if(v->channel<0 || v->channel>3)
return -EINVAL;
v->flags=0;
v->tuners=1;
/* Good question.. its composite or SVHS so.. */
v->type = VIDEO_TYPE_CAMERA;
switch(v->channel)
{
case 0:
strcpy(v->name, "Composite");break;
case 1:
strcpy(v->name, "SVideo");break;
case 2:
strcpy(v->name, "Composite(VCR)");break;
case 3:
strcpy(v->name, "SVideo(VCR)");break;
}
return 0;
}
case VIDIOCSCHAN:
{
struct video_channel *v = arg;
if(v->channel<0 || v->channel>3)
return -EINVAL;
down(&pd->lock);
pms_videosource(v->channel&1);
pms_vcrinput(v->channel>>1);
up(&pd->lock);
return 0;
}
case VIDIOCGTUNER:
{
struct video_tuner *v = arg;
if(v->tuner)
return -EINVAL;
strcpy(v->name, "Format");
v->rangelow=0;
v->rangehigh=0;
v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
switch(standard)
{
case 0:
v->mode = VIDEO_MODE_AUTO;
break;
case 1:
v->mode = VIDEO_MODE_NTSC;
break;
case 2:
v->mode = VIDEO_MODE_PAL;
break;
case 3:
v->mode = VIDEO_MODE_SECAM;
break;
}
return 0;
}
case VIDIOCSTUNER:
{
struct video_tuner *v = arg;
if(v->tuner)
return -EINVAL;
down(&pd->lock);
switch(v->mode)
{
case VIDEO_MODE_AUTO:
pms_framerate(25);
pms_secamcross(0);
pms_format(0);
break;
case VIDEO_MODE_NTSC:
pms_framerate(30);
pms_secamcross(0);
pms_format(1);
break;
case VIDEO_MODE_PAL:
pms_framerate(25);
pms_secamcross(0);
pms_format(2);
break;
case VIDEO_MODE_SECAM:
pms_framerate(25);
pms_secamcross(1);
pms_format(2);
break;
default:
up(&pd->lock);
return -EINVAL;
}
up(&pd->lock);
return 0;
}
case VIDIOCGPICT:
{
struct video_picture *p = arg;
*p = pd->picture;
return 0;
}
case VIDIOCSPICT:
{
struct video_picture *p = arg;
if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16)
||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15)))
return -EINVAL;
pd->picture= *p;
/*
* Now load the card.
*/
down(&pd->lock);
pms_brightness(p->brightness>>8);
pms_hue(p->hue>>8);
pms_colour(p->colour>>8);
pms_contrast(p->contrast>>8);
up(&pd->lock);
return 0;
}
case VIDIOCSWIN:
{
struct video_window *vw = arg;
if(vw->flags)
return -EINVAL;
if(vw->clipcount)
return -EINVAL;
if(vw->height<16||vw->height>480)
return -EINVAL;
if(vw->width<16||vw->width>640)
return -EINVAL;
pd->width=vw->width;
pd->height=vw->height;
down(&pd->lock);
pms_resolution(pd->width, pd->height);
up(&pd->lock); /* Ok we figured out what to use from our wide choice */
return 0;
}
case VIDIOCGWIN:
{
struct video_window *vw = arg;
memset(vw,0,sizeof(*vw));
vw->width=pd->width;
vw->height=pd->height;
return 0;
}
case VIDIOCKEY:
return 0;
case VIDIOCCAPTURE:
case VIDIOCGFBUF:
case VIDIOCSFBUF:
case VIDIOCGFREQ:
case VIDIOCSFREQ:
case VIDIOCGAUDIO:
case VIDIOCSAUDIO:
return -EINVAL;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static int pms_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
return video_usercopy(inode, file, cmd, arg, pms_do_ioctl);
}
static int pms_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
struct video_device *v = video_devdata(file);
struct pms_device *pd=(struct pms_device *)v;
int len;
down(&pd->lock);
len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count);
up(&pd->lock);
return len;
}
static struct file_operations pms_fops = {
.owner = THIS_MODULE,
.open = video_exclusive_open,
.release = video_exclusive_release,
.ioctl = pms_ioctl,
.read = pms_read,
.llseek = no_llseek,
};
static struct video_device pms_template=
{
.owner = THIS_MODULE,
.name = "Mediavision PMS",
.type = VID_TYPE_CAPTURE,
.hardware = VID_HARDWARE_PMS,
.fops = &pms_fops,
};
struct pms_device pms_device;
/*
* Probe for and initialise the Mediavision PMS
*/
static int init_mediavision(void)
{
int id;
int idec, decst;
int i;
unsigned char i2c_defs[]={
0x4C,0x30,0x00,0xE8,
0xB6,0xE2,0x00,0x00,
0xFF,0xFF,0x00,0x00,
0x00,0x00,0x78,0x98,
0x00,0x00,0x00,0x00,
0x34,0x0A,0xF4,0xCE,
0xE4
};
if (!request_region(0x9A01, 1, "Mediavision PMS config"))
{
printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n");
return -EBUSY;
}
if (!request_region(io_port, 3, "Mediavision PMS"))
{
printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port);
release_region(0x9A01, 1);
return -EBUSY;
}
outb(0xB8, 0x9A01); /* Unlock */
outb(io_port>>4, 0x9A01); /* Set IO port */
id=mvv_read(3);
decst=pms_i2c_stat(0x43);
if(decst!=-1)
idec=2;
else if(pms_i2c_stat(0xb9)!=-1)
idec=3;
else if(pms_i2c_stat(0x8b)!=-1)
idec=1;
else
idec=0;
printk(KERN_INFO "PMS type is %d\n", idec);
if(idec == 0) {
release_region(io_port, 3);
release_region(0x9A01, 1);
return -ENODEV;
}
/*
* Ok we have a PMS of some sort
*/
mvv_write(0x04, mem_base>>12); /* Set the memory area */
/* Ok now load the defaults */
for(i=0;i<0x19;i++)
{
if(i2c_defs[i]==0xFF)
pms_i2c_andor(0x8A, i, 0x07,0x00);
else
pms_i2c_write(0x8A, i, i2c_defs[i]);
}
pms_i2c_write(0xB8,0x00,0x12);
pms_i2c_write(0xB8,0x04,0x00);
pms_i2c_write(0xB8,0x07,0x00);
pms_i2c_write(0xB8,0x08,0x00);
pms_i2c_write(0xB8,0x09,0xFF);
pms_i2c_write(0xB8,0x0A,0x00);
pms_i2c_write(0xB8,0x0B,0x10);
pms_i2c_write(0xB8,0x10,0x03);
mvv_write(0x01, 0x00);
mvv_write(0x05, 0xA0);
mvv_write(0x08, 0x25);
mvv_write(0x09, 0x00);
mvv_write(0x0A, 0x20|MVVMEMORYWIDTH);
mvv_write(0x10, 0x02);
mvv_write(0x1E, 0x0C);
mvv_write(0x1F, 0x03);
mvv_write(0x26, 0x06);
mvv_write(0x2B, 0x00);
mvv_write(0x2C, 0x20);
mvv_write(0x2D, 0x00);
mvv_write(0x2F, 0x70);
mvv_write(0x32, 0x00);
mvv_write(0x33, MVVMEMORYWIDTH);
mvv_write(0x34, 0x00);
mvv_write(0x35, 0x00);
mvv_write(0x3A, 0x80);
mvv_write(0x3B, 0x10);
mvv_write(0x20, 0x00);
mvv_write(0x21, 0x00);
mvv_write(0x30, 0x22);
return 0;
}
/*
* Initialization and module stuff
*/
static int __init init_pms_cards(void)
{
printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n");
data_port = io_port +1;
if(init_mediavision())
{
printk(KERN_INFO "Board not found.\n");
return -ENODEV;
}
memcpy(&pms_device, &pms_template, sizeof(pms_template));
init_MUTEX(&pms_device.lock);
pms_device.height=240;
pms_device.width=320;
pms_swsense(75);
pms_resolution(320,240);
return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr);
}
MODULE_PARM(io_port,"i");
MODULE_PARM(mem_base,"i");
MODULE_PARM(video_nr,"i");
MODULE_LICENSE("GPL");
static void __exit shutdown_mediavision(void)
{
release_region(io_port,3);
release_region(0x9A01, 1);
}
static void __exit cleanup_pms_module(void)
{
shutdown_mediavision();
video_unregister_device((struct video_device *)&pms_device);
}
module_init(init_pms_cards);
module_exit(cleanup_pms_module);