2005-04-17 02:20:36 +04:00
/*
hexium_orion . c - v4l2 driver for the Hexium Orion frame grabber cards
Visit http : //www.mihu.de/linux/saa7146/ and follow the link
to " hexium " for further details about this card .
2006-03-17 16:37:02 +03:00
2005-04-17 02:20:36 +04:00
Copyright ( C ) 2003 Michael Hunold < michael @ mihu . de >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2011-08-22 02:56:44 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# define DEBUG_VARIABLE debug
# include <media/saa7146_vv.h>
2011-07-03 22:03:12 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
2008-04-22 21:41:48 +04:00
static int debug ;
2005-04-17 02:20:36 +04:00
module_param ( debug , int , 0 ) ;
MODULE_PARM_DESC ( debug , " debug verbosity " ) ;
/* global variables */
2008-04-22 21:41:48 +04:00
static int hexium_num ;
2005-04-17 02:20:36 +04:00
# define HEXIUM_HV_PCI6_ORION 1
# define HEXIUM_ORION_1SVHS_3BNC 2
# define HEXIUM_ORION_4BNC 3
# define HEXIUM_INPUTS 9
static struct v4l2_input hexium_inputs [ HEXIUM_INPUTS ] = {
2010-12-29 20:29:55 +03:00
{ 0 , " CVBS 1 " , V4L2_INPUT_TYPE_CAMERA , 2 , 0 , V4L2_STD_PAL_BG | V4L2_STD_NTSC_M , 0 , V4L2_IN_CAP_STD } ,
{ 1 , " CVBS 2 " , V4L2_INPUT_TYPE_CAMERA , 2 , 0 , V4L2_STD_PAL_BG | V4L2_STD_NTSC_M , 0 , V4L2_IN_CAP_STD } ,
{ 2 , " CVBS 3 " , V4L2_INPUT_TYPE_CAMERA , 2 , 0 , V4L2_STD_PAL_BG | V4L2_STD_NTSC_M , 0 , V4L2_IN_CAP_STD } ,
{ 3 , " CVBS 4 " , V4L2_INPUT_TYPE_CAMERA , 2 , 0 , V4L2_STD_PAL_BG | V4L2_STD_NTSC_M , 0 , V4L2_IN_CAP_STD } ,
{ 4 , " CVBS 5 " , V4L2_INPUT_TYPE_CAMERA , 2 , 0 , V4L2_STD_PAL_BG | V4L2_STD_NTSC_M , 0 , V4L2_IN_CAP_STD } ,
{ 5 , " CVBS 6 " , V4L2_INPUT_TYPE_CAMERA , 2 , 0 , V4L2_STD_PAL_BG | V4L2_STD_NTSC_M , 0 , V4L2_IN_CAP_STD } ,
{ 6 , " Y/C 1 " , V4L2_INPUT_TYPE_CAMERA , 2 , 0 , V4L2_STD_PAL_BG | V4L2_STD_NTSC_M , 0 , V4L2_IN_CAP_STD } ,
{ 7 , " Y/C 2 " , V4L2_INPUT_TYPE_CAMERA , 2 , 0 , V4L2_STD_PAL_BG | V4L2_STD_NTSC_M , 0 , V4L2_IN_CAP_STD } ,
{ 8 , " Y/C 3 " , V4L2_INPUT_TYPE_CAMERA , 2 , 0 , V4L2_STD_PAL_BG | V4L2_STD_NTSC_M , 0 , V4L2_IN_CAP_STD } ,
2005-04-17 02:20:36 +04:00
} ;
# define HEXIUM_AUDIOS 0
struct hexium_data
{
s8 adr ;
u8 byte ;
} ;
struct hexium
{
int type ;
struct video_device * video_dev ;
2006-03-17 16:37:02 +03:00
struct i2c_adapter i2c_adapter ;
2005-04-17 02:20:36 +04:00
int cur_input ; /* current input */
} ;
/* Philips SAA7110 decoder default registers */
static u8 hexium_saa7110 [ 53 ] = {
/*00*/ 0x4C , 0x3C , 0x0D , 0xEF , 0xBD , 0xF0 , 0x00 , 0x00 ,
/*08*/ 0xF8 , 0xF8 , 0x60 , 0x60 , 0x40 , 0x86 , 0x18 , 0x90 ,
/*10*/ 0x00 , 0x2C , 0x40 , 0x46 , 0x42 , 0x1A , 0xFF , 0xDA ,
/*18*/ 0xF0 , 0x8B , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
/*20*/ 0xD9 , 0x17 , 0x40 , 0x41 , 0x80 , 0x41 , 0x80 , 0x4F ,
/*28*/ 0xFE , 0x01 , 0x0F , 0x0F , 0x03 , 0x01 , 0x81 , 0x03 ,
/*30*/ 0x44 , 0x75 , 0x01 , 0x8C , 0x03
} ;
static struct {
2006-03-17 16:37:02 +03:00
struct hexium_data data [ 8 ] ;
2005-04-17 02:20:36 +04:00
} hexium_input_select [ ] = {
{
{ /* cvbs 1 */
{ 0x06 , 0x00 } ,
{ 0x20 , 0xD9 } ,
{ 0x21 , 0x17 } , // 0x16,
{ 0x22 , 0x40 } ,
{ 0x2C , 0x03 } ,
{ 0x30 , 0x44 } ,
{ 0x31 , 0x75 } , // ??
{ 0x21 , 0x16 } , // 0x03,
}
} , {
{ /* cvbs 2 */
{ 0x06 , 0x00 } ,
{ 0x20 , 0x78 } ,
{ 0x21 , 0x07 } , // 0x03,
{ 0x22 , 0xD2 } ,
{ 0x2C , 0x83 } ,
{ 0x30 , 0x60 } ,
{ 0x31 , 0xB5 } , // ?
{ 0x21 , 0x03 } ,
}
} , {
{ /* cvbs 3 */
{ 0x06 , 0x00 } ,
{ 0x20 , 0xBA } ,
{ 0x21 , 0x07 } , // 0x05,
{ 0x22 , 0x91 } ,
{ 0x2C , 0x03 } ,
{ 0x30 , 0x60 } ,
{ 0x31 , 0xB5 } , // ??
{ 0x21 , 0x05 } , // 0x03,
}
} , {
{ /* cvbs 4 */
{ 0x06 , 0x00 } ,
{ 0x20 , 0xD8 } ,
{ 0x21 , 0x17 } , // 0x16,
{ 0x22 , 0x40 } ,
{ 0x2C , 0x03 } ,
{ 0x30 , 0x44 } ,
{ 0x31 , 0x75 } , // ??
{ 0x21 , 0x16 } , // 0x03,
}
} , {
{ /* cvbs 5 */
{ 0x06 , 0x00 } ,
{ 0x20 , 0xB8 } ,
{ 0x21 , 0x07 } , // 0x05,
{ 0x22 , 0x91 } ,
{ 0x2C , 0x03 } ,
{ 0x30 , 0x60 } ,
{ 0x31 , 0xB5 } , // ??
{ 0x21 , 0x05 } , // 0x03,
}
} , {
{ /* cvbs 6 */
{ 0x06 , 0x00 } ,
{ 0x20 , 0x7C } ,
{ 0x21 , 0x07 } , // 0x03
{ 0x22 , 0xD2 } ,
{ 0x2C , 0x83 } ,
{ 0x30 , 0x60 } ,
{ 0x31 , 0xB5 } , // ??
{ 0x21 , 0x03 } ,
2006-03-17 16:37:02 +03:00
}
2005-04-17 02:20:36 +04:00
} , {
{ /* y/c 1 */
{ 0x06 , 0x80 } ,
{ 0x20 , 0x59 } ,
{ 0x21 , 0x17 } ,
{ 0x22 , 0x42 } ,
{ 0x2C , 0xA3 } ,
{ 0x30 , 0x44 } ,
{ 0x31 , 0x75 } ,
{ 0x21 , 0x12 } ,
}
} , {
{ /* y/c 2 */
{ 0x06 , 0x80 } ,
{ 0x20 , 0x9A } ,
{ 0x21 , 0x17 } ,
{ 0x22 , 0xB1 } ,
{ 0x2C , 0x13 } ,
{ 0x30 , 0x60 } ,
{ 0x31 , 0xB5 } ,
{ 0x21 , 0x14 } ,
}
} , {
{ /* y/c 3 */
{ 0x06 , 0x80 } ,
{ 0x20 , 0x3C } ,
{ 0x21 , 0x27 } ,
{ 0x22 , 0xC1 } ,
{ 0x2C , 0x23 } ,
{ 0x30 , 0x44 } ,
{ 0x31 , 0x75 } ,
{ 0x21 , 0x21 } ,
}
2006-03-17 16:37:02 +03:00
}
2005-04-17 02:20:36 +04:00
} ;
static struct saa7146_standard hexium_standards [ ] = {
{
. name = " PAL " , . id = V4L2_STD_PAL ,
. v_offset = 16 , . v_field = 288 ,
. h_offset = 1 , . h_pixels = 680 ,
. v_max_out = 576 , . h_max_out = 768 ,
} , {
. name = " NTSC " , . id = V4L2_STD_NTSC ,
. v_offset = 16 , . v_field = 240 ,
. h_offset = 1 , . h_pixels = 640 ,
. v_max_out = 480 , . h_max_out = 640 ,
} , {
. name = " SECAM " , . id = V4L2_STD_SECAM ,
. v_offset = 16 , . v_field = 288 ,
. h_offset = 1 , . h_pixels = 720 ,
. v_max_out = 576 , . h_max_out = 768 ,
}
2006-03-17 16:37:02 +03:00
} ;
2005-04-17 02:20:36 +04:00
/* this is only called for old HV-PCI6/Orion cards
without eeprom */
static int hexium_probe ( struct saa7146_dev * dev )
{
struct hexium * hexium = NULL ;
union i2c_smbus_data data ;
int err = 0 ;
2011-08-22 02:56:44 +04:00
DEB_EE ( " \n " ) ;
2005-04-17 02:20:36 +04:00
/* there are no hexium orion cards with revision 0 saa7146s */
if ( 0 = = dev - > revision ) {
return - EFAULT ;
}
2006-01-12 00:40:56 +03:00
hexium = kzalloc ( sizeof ( struct hexium ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( NULL = = hexium ) {
2011-08-22 02:56:44 +04:00
pr_err ( " hexium_probe: not enough kernel memory \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
/* enable i2c-port pins */
saa7146_write ( dev , MC1 , ( MASK_08 | MASK_24 | MASK_10 | MASK_26 ) ) ;
saa7146_write ( dev , DD1_INIT , 0x01000100 ) ;
saa7146_write ( dev , DD1_STREAM_B , 0x00000000 ) ;
saa7146_write ( dev , MC2 , ( MASK_09 | MASK_25 | MASK_10 | MASK_26 ) ) ;
hexium - > i2c_adapter = ( struct i2c_adapter ) {
. name = " hexium orion " ,
} ;
saa7146_i2c_adapter_prepare ( dev , & hexium - > i2c_adapter , SAA7146_I2C_BUS_BIT_RATE_480 ) ;
if ( i2c_add_adapter ( & hexium - > i2c_adapter ) < 0 ) {
2011-08-22 02:56:44 +04:00
DEB_S ( " cannot register i2c-device. skipping. \n " ) ;
2005-04-17 02:20:36 +04:00
kfree ( hexium ) ;
return - EFAULT ;
}
/* set SAA7110 control GPIO 0 */
saa7146_setgpio ( dev , 0 , SAA7146_GPIO_OUTHI ) ;
/* set HWControl GPIO number 2 */
saa7146_setgpio ( dev , 2 , SAA7146_GPIO_OUTHI ) ;
mdelay ( 10 ) ;
/* detect newer Hexium Orion cards by subsystem ids */
if ( 0x17c8 = = dev - > pci - > subsystem_vendor & & 0x0101 = = dev - > pci - > subsystem_device ) {
2011-08-22 02:56:44 +04:00
pr_info ( " device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs \n " ) ;
2005-04-17 02:20:36 +04:00
/* we store the pointer in our private data field */
dev - > ext_priv = hexium ;
hexium - > type = HEXIUM_ORION_1SVHS_3BNC ;
return 0 ;
}
if ( 0x17c8 = = dev - > pci - > subsystem_vendor & & 0x2101 = = dev - > pci - > subsystem_device ) {
2011-08-22 02:56:44 +04:00
pr_info ( " device is a Hexium Orion w/ 4 BNC inputs \n " ) ;
2005-04-17 02:20:36 +04:00
/* we store the pointer in our private data field */
dev - > ext_priv = hexium ;
hexium - > type = HEXIUM_ORION_4BNC ;
return 0 ;
}
2006-03-17 16:37:02 +03:00
/* check if this is an old hexium Orion card by looking at
2005-04-17 02:20:36 +04:00
a saa7110 at address 0x4e */
if ( 0 = = ( err = i2c_smbus_xfer ( & hexium - > i2c_adapter , 0x4e , 0 , I2C_SMBUS_READ , 0x00 , I2C_SMBUS_BYTE_DATA , & data ) ) ) {
2011-08-22 02:56:44 +04:00
pr_info ( " device is a Hexium HV-PCI6/Orion (old) \n " ) ;
2005-04-17 02:20:36 +04:00
/* we store the pointer in our private data field */
dev - > ext_priv = hexium ;
hexium - > type = HEXIUM_HV_PCI6_ORION ;
return 0 ;
}
i2c_del_adapter ( & hexium - > i2c_adapter ) ;
kfree ( hexium ) ;
return - EFAULT ;
}
/* bring hardware to a sane state. this has to be done, just in case someone
wants to capture from this device before it has been properly initialized .
the capture engine would badly fail , because no valid signal arrives on the
saa7146 , thus leading to timeouts and stuff . */
static int hexium_init_done ( struct saa7146_dev * dev )
{
struct hexium * hexium = ( struct hexium * ) dev - > ext_priv ;
union i2c_smbus_data data ;
int i = 0 ;
2011-08-22 02:56:44 +04:00
DEB_D ( " hexium_init_done called \n " ) ;
2005-04-17 02:20:36 +04:00
/* initialize the helper ics to useful values */
for ( i = 0 ; i < sizeof ( hexium_saa7110 ) ; i + + ) {
data . byte = hexium_saa7110 [ i ] ;
if ( 0 ! = i2c_smbus_xfer ( & hexium - > i2c_adapter , 0x4e , 0 , I2C_SMBUS_WRITE , i , I2C_SMBUS_BYTE_DATA , & data ) ) {
2011-08-22 02:56:44 +04:00
pr_err ( " failed for address 0x%02x \n " , i ) ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
}
static int hexium_set_input ( struct hexium * hexium , int input )
{
union i2c_smbus_data data ;
int i = 0 ;
2006-03-17 16:37:02 +03:00
2011-08-22 02:56:44 +04:00
DEB_D ( " \n " ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 8 ; i + + ) {
int adr = hexium_input_select [ input ] . data [ i ] . adr ;
data . byte = hexium_input_select [ input ] . data [ i ] . byte ;
if ( 0 ! = i2c_smbus_xfer ( & hexium - > i2c_adapter , 0x4e , 0 , I2C_SMBUS_WRITE , adr , I2C_SMBUS_BYTE_DATA , & data ) ) {
return - 1 ;
}
2011-08-22 02:56:44 +04:00
pr_debug ( " %d: 0x%02x => 0x%02x \n " , input , adr , data . byte ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2009-01-19 01:59:11 +03:00
static int vidioc_enum_input ( struct file * file , void * fh , struct v4l2_input * i )
{
2011-08-22 02:56:44 +04:00
DEB_EE ( " VIDIOC_ENUMINPUT %d \n " , i - > index ) ;
2009-01-19 01:59:11 +03:00
2009-05-02 23:38:47 +04:00
if ( i - > index > = HEXIUM_INPUTS )
2009-01-19 01:59:11 +03:00
return - EINVAL ;
memcpy ( i , & hexium_inputs [ i - > index ] , sizeof ( struct v4l2_input ) ) ;
2011-08-22 02:56:44 +04:00
DEB_D ( " v4l2_ioctl: VIDIOC_ENUMINPUT %d \n " , i - > index ) ;
2009-01-19 01:59:11 +03:00
return 0 ;
}
static int vidioc_g_input ( struct file * file , void * fh , unsigned int * input )
{
struct saa7146_dev * dev = ( ( struct saa7146_fh * ) fh ) - > dev ;
struct hexium * hexium = ( struct hexium * ) dev - > ext_priv ;
* input = hexium - > cur_input ;
2011-08-22 02:56:44 +04:00
DEB_D ( " VIDIOC_G_INPUT: %d \n " , * input ) ;
2009-01-19 01:59:11 +03:00
return 0 ;
}
static int vidioc_s_input ( struct file * file , void * fh , unsigned int input )
{
struct saa7146_dev * dev = ( ( struct saa7146_fh * ) fh ) - > dev ;
struct hexium * hexium = ( struct hexium * ) dev - > ext_priv ;
2009-10-23 14:59:42 +04:00
if ( input > = HEXIUM_INPUTS )
2009-01-19 01:59:11 +03:00
return - EINVAL ;
hexium - > cur_input = input ;
hexium_set_input ( hexium , input ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static struct saa7146_ext_vv vv_data ;
/* this function only gets called when the probing was successful */
static int hexium_attach ( struct saa7146_dev * dev , struct saa7146_pci_extension_data * info )
{
struct hexium * hexium = ( struct hexium * ) dev - > ext_priv ;
2011-08-22 02:56:44 +04:00
DEB_EE ( " \n " ) ;
2005-04-17 02:20:36 +04:00
saa7146_vv_init ( dev , & vv_data ) ;
2009-01-19 01:59:11 +03:00
vv_data . ops . vidioc_enum_input = vidioc_enum_input ;
vv_data . ops . vidioc_g_input = vidioc_g_input ;
vv_data . ops . vidioc_s_input = vidioc_s_input ;
2005-04-17 02:20:36 +04:00
if ( 0 ! = saa7146_register_device ( & hexium - > video_dev , dev , " hexium orion " , VFL_TYPE_GRABBER ) ) {
2011-08-22 02:56:44 +04:00
pr_err ( " cannot register capture v4l2 device. skipping. \n " ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2011-08-22 02:56:44 +04:00
pr_err ( " found 'hexium orion' frame grabber-%d \n " , hexium_num ) ;
2005-04-17 02:20:36 +04:00
hexium_num + + ;
/* the rest */
hexium - > cur_input = 0 ;
hexium_init_done ( dev ) ;
return 0 ;
}
static int hexium_detach ( struct saa7146_dev * dev )
{
struct hexium * hexium = ( struct hexium * ) dev - > ext_priv ;
2011-08-22 02:56:44 +04:00
DEB_EE ( " dev:%p \n " , dev ) ;
2005-04-17 02:20:36 +04:00
saa7146_unregister_device ( & hexium - > video_dev , dev ) ;
saa7146_vv_release ( dev ) ;
hexium_num - - ;
i2c_del_adapter ( & hexium - > i2c_adapter ) ;
kfree ( hexium ) ;
return 0 ;
}
static int std_callback ( struct saa7146_dev * dev , struct saa7146_standard * std )
{
return 0 ;
}
static struct saa7146_extension extension ;
static struct saa7146_pci_extension_data hexium_hv_pci6 = {
. ext_priv = " Hexium HV-PCI6 / Orion " ,
. ext = & extension ,
} ;
static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = {
. ext_priv = " Hexium HV-PCI6 / Orion (1 SVHS/3 BNC) " ,
. ext = & extension ,
} ;
static struct saa7146_pci_extension_data hexium_orion_4bnc = {
. ext_priv = " Hexium HV-PCI6 / Orion (4 BNC) " ,
. ext = & extension ,
} ;
static struct pci_device_id pci_tbl [ ] = {
{
. vendor = PCI_VENDOR_ID_PHILIPS ,
. device = PCI_DEVICE_ID_PHILIPS_SAA7146 ,
. subvendor = 0x0000 ,
. subdevice = 0x0000 ,
. driver_data = ( unsigned long ) & hexium_hv_pci6 ,
} ,
{
. vendor = PCI_VENDOR_ID_PHILIPS ,
. device = PCI_DEVICE_ID_PHILIPS_SAA7146 ,
. subvendor = 0x17c8 ,
. subdevice = 0x0101 ,
. driver_data = ( unsigned long ) & hexium_orion_1svhs_3bnc ,
} ,
{
. vendor = PCI_VENDOR_ID_PHILIPS ,
. device = PCI_DEVICE_ID_PHILIPS_SAA7146 ,
. subvendor = 0x17c8 ,
. subdevice = 0x2101 ,
. driver_data = ( unsigned long ) & hexium_orion_4bnc ,
} ,
{
. vendor = 0 ,
}
} ;
MODULE_DEVICE_TABLE ( pci , pci_tbl ) ;
static struct saa7146_ext_vv vv_data = {
. inputs = HEXIUM_INPUTS ,
. capabilities = 0 ,
. stds = & hexium_standards [ 0 ] ,
. num_stds = sizeof ( hexium_standards ) / sizeof ( struct saa7146_standard ) ,
. std_callback = & std_callback ,
} ;
static struct saa7146_extension extension = {
2006-02-07 11:49:15 +03:00
. name = " hexium HV-PCI6 Orion " ,
2005-04-17 02:20:36 +04:00
. flags = 0 , // SAA7146_USE_I2C_IRQ,
. pci_tbl = & pci_tbl [ 0 ] ,
. module = THIS_MODULE ,
. probe = hexium_probe ,
. attach = hexium_attach ,
. detach = hexium_detach ,
. irq_mask = 0 ,
. irq_func = NULL ,
} ;
static int __init hexium_init_module ( void )
{
if ( 0 ! = saa7146_register_extension ( & extension ) ) {
2011-08-22 02:56:44 +04:00
DEB_S ( " failed to register extension \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
return 0 ;
}
static void __exit hexium_cleanup_module ( void )
{
saa7146_unregister_extension ( & extension ) ;
}
module_init ( hexium_init_module ) ;
module_exit ( hexium_cleanup_module ) ;
MODULE_DESCRIPTION ( " video4linux-2 driver for Hexium Orion frame grabber cards " ) ;
MODULE_AUTHOR ( " Michael Hunold <michael@mihu.de> " ) ;
MODULE_LICENSE ( " GPL " ) ;