2010-03-13 23:53:58 +03:00
/*
* ngene - cards . c : nGene PCIe bridge driver - card specific info
*
* Copyright ( C ) 2005 - 2007 Micronas
*
* Copyright ( C ) 2008 - 2009 Ralph Metzler < rjkm @ metzlerbros . de >
* Modifications for new nGene firmware ,
* support for EEPROM - copying ,
* support for new dual DVB - S2 card prototype
*
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 only , as published by the Free Software Foundation .
*
*
* 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 . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA
* Or , point your browser to http : //www.gnu.org/copyleft/gpl.html
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/pci_ids.h>
# include "ngene.h"
/* demods/tuners */
# include "stv6110x.h"
# include "stv090x.h"
# include "lnbh24.h"
# include "lgdt330x.h"
# include "mt2131.h"
/****************************************************************************/
/* Demod/tuner attachment ***************************************************/
/****************************************************************************/
static int tuner_attach_stv6110 ( struct ngene_channel * chan )
{
2011-01-10 12:36:13 +03:00
struct i2c_adapter * i2c ;
2010-03-13 23:53:58 +03:00
struct stv090x_config * feconf = ( struct stv090x_config * )
chan - > dev - > card_info - > fe_config [ chan - > number ] ;
struct stv6110x_config * tunerconf = ( struct stv6110x_config * )
chan - > dev - > card_info - > tuner_config [ chan - > number ] ;
struct stv6110x_devctl * ctl ;
2011-01-10 12:36:13 +03:00
/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
if ( chan - > number < 2 )
i2c = & chan - > dev - > channel [ 0 ] . i2c_adapter ;
else
i2c = & chan - > dev - > channel [ 1 ] . i2c_adapter ;
ctl = dvb_attach ( stv6110x_attach , chan - > fe , tunerconf , i2c ) ;
2010-03-13 23:53:58 +03:00
if ( ctl = = NULL ) {
printk ( KERN_ERR DEVICE_NAME " : No STV6110X found! \n " ) ;
return - ENODEV ;
}
feconf - > tuner_init = ctl - > tuner_init ;
2011-01-10 12:36:13 +03:00
feconf - > tuner_sleep = ctl - > tuner_sleep ;
2010-03-13 23:53:58 +03:00
feconf - > tuner_set_mode = ctl - > tuner_set_mode ;
feconf - > tuner_set_frequency = ctl - > tuner_set_frequency ;
feconf - > tuner_get_frequency = ctl - > tuner_get_frequency ;
feconf - > tuner_set_bandwidth = ctl - > tuner_set_bandwidth ;
feconf - > tuner_get_bandwidth = ctl - > tuner_get_bandwidth ;
feconf - > tuner_set_bbgain = ctl - > tuner_set_bbgain ;
feconf - > tuner_get_bbgain = ctl - > tuner_get_bbgain ;
feconf - > tuner_set_refclk = ctl - > tuner_set_refclk ;
feconf - > tuner_get_status = ctl - > tuner_get_status ;
return 0 ;
}
static int demod_attach_stv0900 ( struct ngene_channel * chan )
{
2011-01-10 12:36:13 +03:00
struct i2c_adapter * i2c ;
2010-03-13 23:53:58 +03:00
struct stv090x_config * feconf = ( struct stv090x_config * )
chan - > dev - > card_info - > fe_config [ chan - > number ] ;
2011-01-10 12:36:13 +03:00
/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
/* Note: Both adapters share the same i2c bus, but the demod */
/* driver requires that each demod has its own i2c adapter */
if ( chan - > number < 2 )
i2c = & chan - > dev - > channel [ 0 ] . i2c_adapter ;
else
i2c = & chan - > dev - > channel [ 1 ] . i2c_adapter ;
chan - > fe = dvb_attach ( stv090x_attach , feconf , i2c ,
( chan - > number & 1 ) = = 0 ? STV090x_DEMODULATOR_0
: STV090x_DEMODULATOR_1 ) ;
2010-03-13 23:53:58 +03:00
if ( chan - > fe = = NULL ) {
printk ( KERN_ERR DEVICE_NAME " : No STV0900 found! \n " ) ;
return - ENODEV ;
}
2011-01-10 12:36:13 +03:00
/* store channel info */
if ( feconf - > tuner_i2c_lock )
chan - > fe - > analog_demod_priv = chan ;
if ( ! dvb_attach ( lnbh24_attach , chan - > fe , i2c , 0 ,
2010-03-13 23:53:58 +03:00
0 , chan - > dev - > card_info - > lnb [ chan - > number ] ) ) {
printk ( KERN_ERR DEVICE_NAME " : No LNBH24 found! \n " ) ;
dvb_frontend_detach ( chan - > fe ) ;
return - ENODEV ;
}
return 0 ;
}
2011-01-10 12:36:13 +03:00
static void cineS2_tuner_i2c_lock ( struct dvb_frontend * fe , int lock )
{
struct ngene_channel * chan = fe - > analog_demod_priv ;
if ( lock )
down ( & chan - > dev - > pll_mutex ) ;
else
up ( & chan - > dev - > pll_mutex ) ;
}
static int cineS2_probe ( struct ngene_channel * chan )
{
struct i2c_adapter * i2c ;
struct stv090x_config * fe_conf ;
u8 buf [ 3 ] ;
struct i2c_msg i2c_msg = { . flags = 0 , . buf = buf } ;
int rc ;
/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
if ( chan - > number < 2 )
i2c = & chan - > dev - > channel [ 0 ] . i2c_adapter ;
else
i2c = & chan - > dev - > channel [ 1 ] . i2c_adapter ;
fe_conf = chan - > dev - > card_info - > fe_config [ chan - > number ] ;
i2c_msg . addr = fe_conf - > address ;
/* probe demod */
i2c_msg . len = 2 ;
buf [ 0 ] = 0xf1 ;
buf [ 1 ] = 0x00 ;
rc = i2c_transfer ( i2c , & i2c_msg , 1 ) ;
if ( rc ! = 1 )
return - ENODEV ;
/* demod found, attach it */
rc = demod_attach_stv0900 ( chan ) ;
if ( rc < 0 | | chan - > number < 2 )
return rc ;
/* demod #2: reprogram outputs DPN1 & DPN2 */
i2c_msg . len = 3 ;
buf [ 0 ] = 0xf1 ;
switch ( chan - > number ) {
case 2 :
buf [ 1 ] = 0x5c ;
buf [ 2 ] = 0xc2 ;
break ;
case 3 :
buf [ 1 ] = 0x61 ;
buf [ 2 ] = 0xcc ;
break ;
default :
return - ENODEV ;
}
rc = i2c_transfer ( i2c , & i2c_msg , 1 ) ;
if ( rc ! = 1 ) {
printk ( KERN_ERR DEVICE_NAME " : could not setup DPNx \n " ) ;
return - EIO ;
}
return 0 ;
}
2010-03-13 23:53:58 +03:00
static struct lgdt330x_config aver_m780 = {
. demod_address = 0xb2 > > 1 ,
. demod_chip = LGDT3303 ,
. serial_mpeg = 0x00 , /* PARALLEL */
. clock_polarity_flip = 1 ,
} ;
static struct mt2131_config m780_tunerconfig = {
0xc0 > > 1
} ;
/* A single func to attach the demo and tuner, rather than
* use two sep funcs like the current design mandates .
*/
static int demod_attach_lg330x ( struct ngene_channel * chan )
{
chan - > fe = dvb_attach ( lgdt330x_attach , & aver_m780 , & chan - > i2c_adapter ) ;
if ( chan - > fe = = NULL ) {
printk ( KERN_ERR DEVICE_NAME " : No LGDT330x found! \n " ) ;
return - ENODEV ;
}
dvb_attach ( mt2131_attach , chan - > fe , & chan - > i2c_adapter ,
& m780_tunerconfig , 0 ) ;
return ( chan - > fe ) ? 0 : - ENODEV ;
}
/****************************************************************************/
/* Switch control (I2C gates, etc.) *****************************************/
/****************************************************************************/
static struct stv090x_config fe_cineS2 = {
. device = STV0900 ,
. demod_mode = STV090x_DUAL ,
. clk_mode = STV090x_CLK_EXT ,
. xtal = 27000000 ,
. address = 0x68 ,
. ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED ,
. ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED ,
. repeater_level = STV090x_RPTLEVEL_16 ,
. adc1_range = STV090x_ADC_1Vpp ,
. adc2_range = STV090x_ADC_1Vpp ,
. diseqc_envelope_mode = true ,
2011-01-10 12:36:13 +03:00
. tuner_i2c_lock = cineS2_tuner_i2c_lock ,
} ;
static struct stv090x_config fe_cineS2_2 = {
. device = STV0900 ,
. demod_mode = STV090x_DUAL ,
. clk_mode = STV090x_CLK_EXT ,
. xtal = 27000000 ,
. address = 0x69 ,
. ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED ,
. ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED ,
. repeater_level = STV090x_RPTLEVEL_16 ,
. adc1_range = STV090x_ADC_1Vpp ,
. adc2_range = STV090x_ADC_1Vpp ,
. diseqc_envelope_mode = true ,
. tuner_i2c_lock = cineS2_tuner_i2c_lock ,
2010-03-13 23:53:58 +03:00
} ;
static struct stv6110x_config tuner_cineS2_0 = {
. addr = 0x60 ,
. refclk = 27000000 ,
. clk_div = 1 ,
} ;
static struct stv6110x_config tuner_cineS2_1 = {
. addr = 0x63 ,
. refclk = 27000000 ,
. clk_div = 1 ,
} ;
static struct ngene_info ngene_info_cineS2 = {
. type = NGENE_SIDEWINDER ,
. name = " Linux4Media cineS2 DVB-S2 Twin Tuner " ,
. io_type = { NGENE_IO_TSIN , NGENE_IO_TSIN } ,
. demod_attach = { demod_attach_stv0900 , demod_attach_stv0900 } ,
. tuner_attach = { tuner_attach_stv6110 , tuner_attach_stv6110 } ,
. fe_config = { & fe_cineS2 , & fe_cineS2 } ,
. tuner_config = { & tuner_cineS2_0 , & tuner_cineS2_1 } ,
. lnb = { 0x0b , 0x08 } ,
. tsf = { 3 , 3 } ,
2011-01-10 12:36:13 +03:00
. fw_version = 18 ,
. msi_supported = true ,
2010-03-13 23:53:58 +03:00
} ;
static struct ngene_info ngene_info_satixS2 = {
. type = NGENE_SIDEWINDER ,
. name = " Mystique SaTiX-S2 Dual " ,
. io_type = { NGENE_IO_TSIN , NGENE_IO_TSIN } ,
. demod_attach = { demod_attach_stv0900 , demod_attach_stv0900 } ,
. tuner_attach = { tuner_attach_stv6110 , tuner_attach_stv6110 } ,
. fe_config = { & fe_cineS2 , & fe_cineS2 } ,
. tuner_config = { & tuner_cineS2_0 , & tuner_cineS2_1 } ,
. lnb = { 0x0b , 0x08 } ,
. tsf = { 3 , 3 } ,
2011-01-10 12:36:13 +03:00
. fw_version = 18 ,
. msi_supported = true ,
2010-03-13 23:53:58 +03:00
} ;
static struct ngene_info ngene_info_satixS2v2 = {
. type = NGENE_SIDEWINDER ,
. name = " Mystique SaTiX-S2 Dual (v2) " ,
2011-01-10 12:36:13 +03:00
. io_type = { NGENE_IO_TSIN , NGENE_IO_TSIN , NGENE_IO_TSIN , NGENE_IO_TSIN } ,
. demod_attach = { demod_attach_stv0900 , demod_attach_stv0900 , cineS2_probe , cineS2_probe } ,
. tuner_attach = { tuner_attach_stv6110 , tuner_attach_stv6110 , tuner_attach_stv6110 , tuner_attach_stv6110 } ,
. fe_config = { & fe_cineS2 , & fe_cineS2 , & fe_cineS2_2 , & fe_cineS2_2 } ,
. tuner_config = { & tuner_cineS2_0 , & tuner_cineS2_1 , & tuner_cineS2_0 , & tuner_cineS2_1 } ,
. lnb = { 0x0a , 0x08 , 0x0b , 0x09 } ,
2010-03-13 23:53:58 +03:00
. tsf = { 3 , 3 } ,
2011-01-10 12:36:13 +03:00
. fw_version = 18 ,
. msi_supported = true ,
2010-03-13 23:53:58 +03:00
} ;
static struct ngene_info ngene_info_cineS2v5 = {
. type = NGENE_SIDEWINDER ,
. name = " Linux4Media cineS2 DVB-S2 Twin Tuner (v5) " ,
2011-01-10 12:36:13 +03:00
. io_type = { NGENE_IO_TSIN , NGENE_IO_TSIN , NGENE_IO_TSIN , NGENE_IO_TSIN } ,
. demod_attach = { demod_attach_stv0900 , demod_attach_stv0900 , cineS2_probe , cineS2_probe } ,
. tuner_attach = { tuner_attach_stv6110 , tuner_attach_stv6110 , tuner_attach_stv6110 , tuner_attach_stv6110 } ,
. fe_config = { & fe_cineS2 , & fe_cineS2 , & fe_cineS2_2 , & fe_cineS2_2 } ,
. tuner_config = { & tuner_cineS2_0 , & tuner_cineS2_1 , & tuner_cineS2_0 , & tuner_cineS2_1 } ,
. lnb = { 0x0a , 0x08 , 0x0b , 0x09 } ,
2010-03-13 23:53:58 +03:00
. tsf = { 3 , 3 } ,
2011-01-10 12:36:13 +03:00
. fw_version = 18 ,
. msi_supported = true ,
2010-03-13 23:53:58 +03:00
} ;
2011-01-10 12:36:13 +03:00
2010-05-16 12:08:49 +04:00
static struct ngene_info ngene_info_duoFlexS2 = {
. type = NGENE_SIDEWINDER ,
. name = " Digital Devices DuoFlex S2 miniPCIe " ,
2011-01-10 12:36:13 +03:00
. io_type = { NGENE_IO_TSIN , NGENE_IO_TSIN , NGENE_IO_TSIN , NGENE_IO_TSIN } ,
. demod_attach = { cineS2_probe , cineS2_probe , cineS2_probe , cineS2_probe } ,
. tuner_attach = { tuner_attach_stv6110 , tuner_attach_stv6110 , tuner_attach_stv6110 , tuner_attach_stv6110 } ,
. fe_config = { & fe_cineS2 , & fe_cineS2 , & fe_cineS2_2 , & fe_cineS2_2 } ,
. tuner_config = { & tuner_cineS2_0 , & tuner_cineS2_1 , & tuner_cineS2_0 , & tuner_cineS2_1 } ,
. lnb = { 0x0a , 0x08 , 0x0b , 0x09 } ,
2010-05-16 12:08:49 +04:00
. tsf = { 3 , 3 } ,
2011-01-10 12:36:13 +03:00
. fw_version = 18 ,
. msi_supported = true ,
2010-05-16 12:08:49 +04:00
} ;
2010-03-13 23:53:58 +03:00
static struct ngene_info ngene_info_m780 = {
. type = NGENE_APP ,
. name = " Aver M780 ATSC/QAM-B " ,
/* Channel 0 is analog, which is currently unsupported */
. io_type = { NGENE_IO_NONE , NGENE_IO_TSIN } ,
. demod_attach = { NULL , demod_attach_lg330x } ,
/* Ensure these are NULL else the frame will call them (as funcs) */
. tuner_attach = { 0 , 0 , 0 , 0 } ,
. fe_config = { NULL , & aver_m780 } ,
. avf = { 0 } ,
/* A custom electrical interface config for the demod to bridge */
. tsf = { 4 , 4 } ,
. fw_version = 15 ,
} ;
/****************************************************************************/
/****************************************************************************/
/* PCI Subsystem ID *********************************************************/
/****************************************************************************/
# define NGENE_ID(_subvend, _subdev, _driverdata) { \
. vendor = NGENE_VID , . device = NGENE_PID , \
. subvendor = _subvend , . subdevice = _subdev , \
. driver_data = ( unsigned long ) & _driverdata }
/****************************************************************************/
static const struct pci_device_id ngene_id_tbl [ ] __devinitdata = {
NGENE_ID ( 0x18c3 , 0xabc3 , ngene_info_cineS2 ) ,
NGENE_ID ( 0x18c3 , 0xabc4 , ngene_info_cineS2 ) ,
NGENE_ID ( 0x18c3 , 0xdb01 , ngene_info_satixS2 ) ,
NGENE_ID ( 0x18c3 , 0xdb02 , ngene_info_satixS2v2 ) ,
NGENE_ID ( 0x18c3 , 0xdd00 , ngene_info_cineS2v5 ) ,
2010-05-16 12:08:49 +04:00
NGENE_ID ( 0x18c3 , 0xdd10 , ngene_info_duoFlexS2 ) ,
NGENE_ID ( 0x18c3 , 0xdd20 , ngene_info_duoFlexS2 ) ,
2010-03-13 23:53:58 +03:00
NGENE_ID ( 0x1461 , 0x062e , ngene_info_m780 ) ,
{ 0 }
} ;
MODULE_DEVICE_TABLE ( pci , ngene_id_tbl ) ;
/****************************************************************************/
/* Init/Exit ****************************************************************/
/****************************************************************************/
static pci_ers_result_t ngene_error_detected ( struct pci_dev * dev ,
enum pci_channel_state state )
{
printk ( KERN_ERR DEVICE_NAME " : PCI error \n " ) ;
if ( state = = pci_channel_io_perm_failure )
return PCI_ERS_RESULT_DISCONNECT ;
if ( state = = pci_channel_io_frozen )
return PCI_ERS_RESULT_NEED_RESET ;
return PCI_ERS_RESULT_CAN_RECOVER ;
}
static pci_ers_result_t ngene_link_reset ( struct pci_dev * dev )
{
printk ( KERN_INFO DEVICE_NAME " : link reset \n " ) ;
return 0 ;
}
static pci_ers_result_t ngene_slot_reset ( struct pci_dev * dev )
{
printk ( KERN_INFO DEVICE_NAME " : slot reset \n " ) ;
return 0 ;
}
static void ngene_resume ( struct pci_dev * dev )
{
printk ( KERN_INFO DEVICE_NAME " : resume \n " ) ;
}
static struct pci_error_handlers ngene_errors = {
. error_detected = ngene_error_detected ,
. link_reset = ngene_link_reset ,
. slot_reset = ngene_slot_reset ,
. resume = ngene_resume ,
} ;
static struct pci_driver ngene_pci_driver = {
. name = " ngene " ,
. id_table = ngene_id_tbl ,
. probe = ngene_probe ,
. remove = __devexit_p ( ngene_remove ) ,
. err_handler = & ngene_errors ,
} ;
static __init int module_init_ngene ( void )
{
printk ( KERN_INFO
" nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas \n " ) ;
return pci_register_driver ( & ngene_pci_driver ) ;
}
static __exit void module_exit_ngene ( void )
{
pci_unregister_driver ( & ngene_pci_driver ) ;
}
module_init ( module_init_ngene ) ;
module_exit ( module_exit_ngene ) ;
MODULE_DESCRIPTION ( " nGene " ) ;
MODULE_AUTHOR ( " Micronas, Ralph Metzler, Manfred Voelkel " ) ;
MODULE_LICENSE ( " GPL " ) ;