2005-04-17 02:20:36 +04:00
/*
* 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 . . .
2006-03-25 15:19:53 +03:00
*
2005-04-17 02:20:36 +04:00
* Changes :
* 08 / 07 / 2003 Daniele Bellucci < bellucda @ tiscali . it >
2006-03-25 15:19:53 +03:00
* - pms_capture : report back - EFAULT
2005-04-17 02:20:36 +04:00
*/
# 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/videodev.h>
2006-06-05 17:26:32 +04:00
# include <media/v4l2-common.h>
2008-07-20 15:12:02 +04:00
# include <media/v4l2-ioctl.h>
2006-02-07 11:49:14 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# 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 ;
2008-08-23 12:31:47 +04:00
unsigned long in_use ;
2006-02-07 11:49:14 +03:00
struct mutex lock ;
2005-04-17 02:20:36 +04:00
} ;
struct i2c_info
{
u8 slave ;
u8 sub ;
u8 data ;
u8 hits ;
} ;
2008-04-22 21:41:48 +04:00
static int i2c_count ;
2005-04-17 02:20:36 +04:00
static struct i2c_info i2cinfo [ 64 ] ;
static int decoder = PHILIPS2 ;
2008-04-22 21:41:48 +04:00
static int standard ; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
2005-04-17 02:20:36 +04:00
/*
* I / O ports and Shared Memory
*/
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
static int io_port = 0x250 ;
static int data_port = 0x251 ;
static int mem_base = 0xC8000 ;
static void __iomem * mem ;
static int video_nr = - 1 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
outb ( 0x28 , io_port ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
counter = 0 ;
while ( ( inb ( data_port ) & 0x01 ) = = 0 )
if ( counter + + = = 256 )
break ;
while ( ( inb ( data_port ) & 0x01 ) ! = 0 )
if ( counter + + = = 256 )
break ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
outb ( slave , io_port ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
counter = 0 ;
while ( ( inb ( data_port ) & 0x01 ) = = 0 )
if ( counter + + = = 256 )
break ;
while ( ( inb ( data_port ) & 0x01 ) ! = 0 )
if ( counter + + = = 256 )
break ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 ) ;
2006-03-25 15:19:53 +03:00
return inb ( data_port ) ;
2005-04-17 02:20:36 +04:00
}
static int pms_i2c_write ( u16 slave , u16 sub , u16 data )
{
int skip = 0 ;
int count ;
int i ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < i2c_count ; i + + )
{
if ( ( i2cinfo [ i ] . slave = = slave ) & &
( i2cinfo [ i ] . sub = = sub ) )
{
2006-03-25 15:19:53 +03:00
if ( i2cinfo [ i ] . data = = data )
skip = 1 ;
i2cinfo [ i ] . data = data ;
i = i2c_count + 1 ;
2005-04-17 02:20:36 +04:00
}
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( i = = i2c_count & & i2c_count < 64 )
{
i2cinfo [ i2c_count ] . slave = slave ;
i2cinfo [ i2c_count ] . sub = sub ;
i2cinfo [ i2c_count ] . data = data ;
i2c_count + + ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( skip )
return 0 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
mvv_write ( 0x29 , sub ) ;
mvv_write ( 0x2A , data ) ;
mvv_write ( 0x28 , slave ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
outb ( 0x28 , io_port ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
count = 0 ;
while ( ( inb ( data_port ) & 1 ) = = 0 )
if ( count > 255 )
break ;
while ( ( inb ( data_port ) & 1 ) ! = 0 )
if ( count > 255 )
break ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
count = inb ( data_port ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 )
{
2006-03-25 15:19:53 +03:00
u8 tmp ;
2005-04-17 02:20:36 +04:00
tmp = pms_i2c_read ( slave , sub ) ;
tmp = ( tmp & and ) | or ;
pms_i2c_write ( slave , sub , tmp ) ;
}
/*
* Control functions
*/
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 ;
}
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( decoder = = PHILIPS1 )
target = 0x42 ;
else if ( decoder = = PHILIPS2 )
target = 0x8A ;
else
return ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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
2006-03-25 15:19:53 +03:00
* could become a private v4l ioctl for PMSCONFIG or somesuch if
2005-04-17 02:20:36 +04:00
* 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
*/
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 16 bit ratios into best small ratio the chipset can grok
*/
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 */
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
while ( ( ( decinum | deciden ) & 1 ) = = 0 )
{
decinum > > = 1 ;
deciden > > = 1 ;
}
while ( deciden > 32 )
{
deciden > > = 1 ;
decinum = ( decinum + 1 ) > > 1 ;
}
if ( deciden = = 32 )
deciden - - ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
mvv_write ( 0x24 , 0x80 | deciden ) ;
mvv_write ( 0x25 , decinum ) ;
}
static void pms_resolution ( short width , short height )
{
int fg_height ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
fg_height = height ;
if ( fg_height > 280 )
fg_height = 280 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
mvv_write ( 0x18 , fg_height ) ;
mvv_write ( 0x19 , fg_height > > 8 ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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
*/
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 __user * buf , int rgb555 , int count )
{
int y ;
int dw = 2 * dev - > width ;
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); */
2006-03-25 15:19:53 +03:00
for ( y = 0 ; y < dev - > height ; y + + )
2005-04-17 02:20:36 +04:00
{
writeb ( 0 , mem ) ; /* synchronisiert neue Zeile */
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/*
* This is in truth a fifo , be very careful as if you
* forgot this odd things will occur 8 )
*/
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
memcpy_fromio ( tmp , mem , dw + 32 ) ; /* discard 16 word */
cnt - = dev - > height ;
2006-03-25 15:19:53 +03:00
while ( cnt < = 0 )
{
2005-04-17 02:20:36 +04:00
/*
* Don ' t copy too far
*/
int dt = dw ;
if ( dt + len > count )
dt = count - len ;
cnt + = dev - > height ;
if ( copy_to_user ( buf , tmp + 32 , dt ) )
return len ? len : - EFAULT ;
2006-03-25 15:19:53 +03:00
buf + = dt ;
2005-04-17 02:20:36 +04:00
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 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 ;
2006-02-07 11:49:14 +03:00
mutex_lock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
pms_videosource ( v - > channel & 1 ) ;
pms_vcrinput ( v - > channel > > 1 ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
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 ;
2006-02-07 11:49:14 +03:00
mutex_lock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
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 :
2006-02-07 11:49:14 +03:00
mutex_unlock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2006-02-07 11:49:14 +03:00
mutex_unlock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
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 ) ) )
2006-06-20 07:30:57 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
pd - > picture = * p ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/*
* Now load the card .
*/
2006-02-07 11:49:14 +03:00
mutex_lock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
pms_brightness ( p - > brightness > > 8 ) ;
pms_hue ( p - > hue > > 8 ) ;
pms_colour ( p - > colour > > 8 ) ;
2006-03-25 15:19:53 +03:00
pms_contrast ( p - > contrast > > 8 ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
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 ;
2006-02-07 11:49:14 +03:00
mutex_lock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
pms_resolution ( pd - > width , pd - > height ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & pd - > lock ) ; /* Ok we figured out what to use from our wide choice */
2005-04-17 02:20:36 +04:00
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 ssize_t pms_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
struct video_device * v = video_devdata ( file ) ;
struct pms_device * pd = ( struct pms_device * ) v ;
int len ;
2006-03-25 15:19:53 +03:00
2006-02-07 11:49:14 +03:00
mutex_lock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
len = pms_capture ( pd , buf , ( pd - > picture . depth = = 16 ) ? 0 : 1 , count ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & pd - > lock ) ;
2005-04-17 02:20:36 +04:00
return len ;
}
2008-08-23 12:31:47 +04:00
static int pms_exclusive_open ( struct inode * inode , struct file * file )
{
struct video_device * v = video_devdata ( file ) ;
struct pms_device * pd = ( struct pms_device * ) v ;
return test_and_set_bit ( 0 , & pd - > in_use ) ? - EBUSY : 0 ;
}
static int pms_exclusive_release ( struct inode * inode , struct file * file )
{
struct video_device * v = video_devdata ( file ) ;
struct pms_device * pd = ( struct pms_device * ) v ;
clear_bit ( 0 , & pd - > in_use ) ;
return 0 ;
}
2007-02-12 11:55:33 +03:00
static const struct file_operations pms_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
2008-08-23 12:31:47 +04:00
. open = pms_exclusive_open ,
. release = pms_exclusive_release ,
2005-04-17 02:20:36 +04:00
. ioctl = pms_ioctl ,
2008-04-22 21:46:11 +04:00
# ifdef CONFIG_COMPAT
2006-01-09 20:24:57 +03:00
. compat_ioctl = v4l_compat_ioctl32 ,
2008-04-22 21:46:11 +04:00
# endif
2005-04-17 02:20:36 +04:00
. read = pms_read ,
. llseek = no_llseek ,
} ;
static struct video_device pms_template =
{
. name = " Mediavision PMS " ,
. fops = & pms_fops ,
2008-08-23 13:23:55 +04:00
. release = video_device_release_empty ,
2005-04-17 02:20:36 +04:00
} ;
static struct pms_device pms_device ;
/*
* Probe for and initialise the Mediavision PMS
*/
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
static int init_mediavision ( void )
{
int id ;
int idec , decst ;
int i ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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
} ;
mem = ioremap ( mem_base , 0x800 ) ;
if ( ! mem )
return - ENOMEM ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( ! request_region ( 0x9A01 , 1 , " Mediavision PMS config " ) )
{
printk ( KERN_WARNING " mediavision: unable to detect: 0x9A01 in use. \n " ) ;
iounmap ( mem ) ;
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 ) ;
iounmap ( mem ) ;
return - EBUSY ;
}
outb ( 0xB8 , 0x9A01 ) ; /* Unlock */
outb ( io_port > > 4 , 0x9A01 ) ; /* Set IO port */
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
id = mvv_read ( 3 ) ;
decst = pms_i2c_stat ( 0x43 ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( decst ! = - 1 )
idec = 2 ;
else if ( pms_i2c_stat ( 0xb9 ) ! = - 1 )
idec = 3 ;
else if ( pms_i2c_stat ( 0x8b ) ! = - 1 )
idec = 1 ;
2006-03-25 15:19:53 +03:00
else
2005-04-17 02:20:36 +04:00
idec = 0 ;
printk ( KERN_INFO " PMS type is %d \n " , idec ) ;
if ( idec = = 0 ) {
release_region ( io_port , 3 ) ;
release_region ( 0x9A01 , 1 ) ;
iounmap ( mem ) ;
return - ENODEV ;
}
/*
* Ok we have a PMS of some sort
*/
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
mvv_write ( 0x04 , mem_base > > 12 ) ; /* Set the memory area */
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/* Ok now load the defaults */
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 ] ) ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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 ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
mvv_write ( 0x01 , 0x00 ) ;
mvv_write ( 0x05 , 0xA0 ) ;
mvv_write ( 0x08 , 0x25 ) ;
mvv_write ( 0x09 , 0x00 ) ;
2006-03-25 15:19:53 +03:00
mvv_write ( 0x0A , 0x20 | MVVMEMORYWIDTH ) ;
2005-04-17 02:20:36 +04:00
mvv_write ( 0x10 , 0x02 ) ;
mvv_write ( 0x1E , 0x0C ) ;
mvv_write ( 0x1F , 0x03 ) ;
mvv_write ( 0x26 , 0x06 ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
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
*/
2006-03-25 15:19:53 +03:00
2008-09-03 23:48:21 +04:00
# ifndef MODULE
static int enable ;
module_param ( enable , int , 0 ) ;
# endif
2005-04-17 02:20:36 +04:00
static int __init init_pms_cards ( void )
{
printk ( KERN_INFO " Mediavision Pro Movie Studio driver 0.02 \n " ) ;
2006-03-25 15:19:53 +03:00
2008-09-03 23:48:21 +04:00
# ifndef MODULE
if ( ! enable ) {
2008-09-03 23:48:23 +04:00
printk ( KERN_INFO " PMS: not enabled, use pms.enable=1 to "
" probe \n " ) ;
2008-09-03 23:48:21 +04:00
return - ENODEV ;
}
# endif
2005-04-17 02:20:36 +04:00
data_port = io_port + 1 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( init_mediavision ( ) )
{
printk ( KERN_INFO " Board not found. \n " ) ;
return - ENODEV ;
}
memcpy ( & pms_device , & pms_template , sizeof ( pms_template ) ) ;
2006-02-07 11:49:14 +03:00
mutex_init ( & pms_device . lock ) ;
2005-04-17 02:20:36 +04:00
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_param ( io_port , int , 0 ) ;
module_param ( mem_base , int , 0 ) ;
module_param ( video_nr , int , 0 ) ;
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 ) ;
iounmap ( mem ) ;
}
module_init ( init_pms_cards ) ;
module_exit ( cleanup_pms_module ) ;