2017-07-09 15:42:44 -04:00
/*
2017-10-15 16:51:54 -04:00
* ddbridge - max . c : Digital Devices bridge MAX card support
2017-07-09 15:42:44 -04:00
*
* Copyright ( C ) 2010 - 2017 Digital Devices GmbH
* Ralph Metzler < rjkm @ metzlerbros . de >
* Marcus Metzler < mocm @ metzlerbros . de >
*
* 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 .
*
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/poll.h>
# include <linux/io.h>
# include <linux/pci.h>
# include <linux/pci_ids.h>
# include <linux/timer.h>
# include <linux/i2c.h>
# include <linux/swab.h>
# include <linux/vmalloc.h>
# include "ddbridge.h"
# include "ddbridge-regs.h"
# include "ddbridge-io.h"
2017-10-15 16:51:54 -04:00
# include "ddbridge-max.h"
2017-07-09 15:42:44 -04:00
# include "mxl5xx.h"
/******************************************************************************/
/* MaxS4/8 related modparams */
static int fmode ;
module_param ( fmode , int , 0444 ) ;
MODULE_PARM_DESC ( fmode , " frontend emulation mode " ) ;
static int fmode_sat = - 1 ;
module_param ( fmode_sat , int , 0444 ) ;
MODULE_PARM_DESC ( fmode_sat , " set frontend emulation mode sat " ) ;
static int old_quattro ;
module_param ( old_quattro , int , 0444 ) ;
MODULE_PARM_DESC ( old_quattro , " old quattro LNB input order " ) ;
/******************************************************************************/
static int lnb_command ( struct ddb * dev , u32 link , u32 lnb , u32 cmd )
{
u32 c , v = 0 , tag = DDB_LINK_TAG ( link ) ;
v = LNB_TONE & ( dev - > link [ link ] . lnb . tone < < ( 15 - lnb ) ) ;
ddbwritel ( dev , cmd | v , tag | LNB_CONTROL ( lnb ) ) ;
for ( c = 0 ; c < 10 ; c + + ) {
v = ddbreadl ( dev , tag | LNB_CONTROL ( lnb ) ) ;
if ( ( v & LNB_BUSY ) = = 0 )
break ;
msleep ( 20 ) ;
}
if ( c = = 10 )
dev_info ( dev - > dev , " %s lnb = %08x cmd = %08x \n " ,
2017-10-15 16:51:51 -04:00
__func__ , lnb , cmd ) ;
2017-07-09 15:42:44 -04:00
return 0 ;
}
static int max_send_master_cmd ( struct dvb_frontend * fe ,
struct dvb_diseqc_master_cmd * cmd )
{
struct ddb_input * input = fe - > sec_priv ;
struct ddb_port * port = input - > port ;
struct ddb * dev = port - > dev ;
struct ddb_dvb * dvb = & port - > dvb [ input - > nr & 1 ] ;
u32 tag = DDB_LINK_TAG ( port - > lnr ) ;
int i ;
u32 fmode = dev - > link [ port - > lnr ] . lnb . fmode ;
if ( fmode = = 2 | | fmode = = 1 )
return 0 ;
if ( dvb - > diseqc_send_master_cmd )
dvb - > diseqc_send_master_cmd ( fe , cmd ) ;
mutex_lock ( & dev - > link [ port - > lnr ] . lnb . lock ) ;
ddbwritel ( dev , 0 , tag | LNB_BUF_LEVEL ( dvb - > input ) ) ;
for ( i = 0 ; i < cmd - > msg_len ; i + + )
ddbwritel ( dev , cmd - > msg [ i ] , tag | LNB_BUF_WRITE ( dvb - > input ) ) ;
lnb_command ( dev , port - > lnr , dvb - > input , LNB_CMD_DISEQC ) ;
mutex_unlock ( & dev - > link [ port - > lnr ] . lnb . lock ) ;
return 0 ;
}
static int lnb_send_diseqc ( struct ddb * dev , u32 link , u32 input ,
struct dvb_diseqc_master_cmd * cmd )
{
u32 tag = DDB_LINK_TAG ( link ) ;
int i ;
ddbwritel ( dev , 0 , tag | LNB_BUF_LEVEL ( input ) ) ;
for ( i = 0 ; i < cmd - > msg_len ; i + + )
ddbwritel ( dev , cmd - > msg [ i ] , tag | LNB_BUF_WRITE ( input ) ) ;
lnb_command ( dev , link , input , LNB_CMD_DISEQC ) ;
return 0 ;
}
static int lnb_set_sat ( struct ddb * dev , u32 link , u32 input , u32 sat , u32 band ,
u32 hor )
{
struct dvb_diseqc_master_cmd cmd = {
. msg = { 0xe0 , 0x10 , 0x38 , 0xf0 , 0x00 , 0x00 } ,
. msg_len = 4
} ;
cmd . msg [ 3 ] = 0xf0 | ( ( ( sat < < 2 ) & 0x0c ) | ( band ? 1 : 0 ) |
( hor ? 2 : 0 ) ) ;
return lnb_send_diseqc ( dev , link , input , & cmd ) ;
}
static int lnb_set_tone ( struct ddb * dev , u32 link , u32 input ,
2017-10-15 16:51:51 -04:00
enum fe_sec_tone_mode tone )
2017-07-09 15:42:44 -04:00
{
int s = 0 ;
u32 mask = ( 1ULL < < input ) ;
switch ( tone ) {
case SEC_TONE_OFF :
if ( ! ( dev - > link [ link ] . lnb . tone & mask ) )
return 0 ;
dev - > link [ link ] . lnb . tone & = ~ ( 1ULL < < input ) ;
break ;
case SEC_TONE_ON :
if ( dev - > link [ link ] . lnb . tone & mask )
return 0 ;
dev - > link [ link ] . lnb . tone | = ( 1ULL < < input ) ;
break ;
default :
s = - EINVAL ;
break ;
2017-08-20 10:17:15 -04:00
}
2017-07-09 15:42:44 -04:00
if ( ! s )
s = lnb_command ( dev , link , input , LNB_CMD_NOP ) ;
return s ;
}
static int lnb_set_voltage ( struct ddb * dev , u32 link , u32 input ,
2017-10-15 16:51:51 -04:00
enum fe_sec_voltage voltage )
2017-07-09 15:42:44 -04:00
{
int s = 0 ;
if ( dev - > link [ link ] . lnb . oldvoltage [ input ] = = voltage )
return 0 ;
switch ( voltage ) {
case SEC_VOLTAGE_OFF :
if ( dev - > link [ link ] . lnb . voltage [ input ] )
return 0 ;
lnb_command ( dev , link , input , LNB_CMD_OFF ) ;
break ;
case SEC_VOLTAGE_13 :
lnb_command ( dev , link , input , LNB_CMD_LOW ) ;
break ;
case SEC_VOLTAGE_18 :
lnb_command ( dev , link , input , LNB_CMD_HIGH ) ;
break ;
default :
s = - EINVAL ;
break ;
2017-08-20 10:17:15 -04:00
}
2017-07-09 15:42:44 -04:00
dev - > link [ link ] . lnb . oldvoltage [ input ] = voltage ;
return s ;
}
static int max_set_input_unlocked ( struct dvb_frontend * fe , int in )
{
struct ddb_input * input = fe - > sec_priv ;
struct ddb_port * port = input - > port ;
struct ddb * dev = port - > dev ;
struct ddb_dvb * dvb = & port - > dvb [ input - > nr & 1 ] ;
int res = 0 ;
if ( in > 3 )
return - EINVAL ;
if ( dvb - > input ! = in ) {
u32 bit = ( 1ULL < < input - > nr ) ;
2017-07-09 15:42:45 -04:00
u32 obit =
dev - > link [ port - > lnr ] . lnb . voltage [ dvb - > input & 3 ] & bit ;
2017-07-09 15:42:44 -04:00
2017-07-09 15:42:45 -04:00
dev - > link [ port - > lnr ] . lnb . voltage [ dvb - > input & 3 ] & = ~ bit ;
2017-07-09 15:42:44 -04:00
dvb - > input = in ;
2017-07-09 15:42:45 -04:00
dev - > link [ port - > lnr ] . lnb . voltage [ dvb - > input & 3 ] | = obit ;
2017-07-09 15:42:44 -04:00
}
res = dvb - > set_input ( fe , in ) ;
return res ;
}
static int max_set_tone ( struct dvb_frontend * fe , enum fe_sec_tone_mode tone )
{
struct ddb_input * input = fe - > sec_priv ;
struct ddb_port * port = input - > port ;
struct ddb * dev = port - > dev ;
struct ddb_dvb * dvb = & port - > dvb [ input - > nr & 1 ] ;
int tuner = 0 ;
int res = 0 ;
u32 fmode = dev - > link [ port - > lnr ] . lnb . fmode ;
mutex_lock ( & dev - > link [ port - > lnr ] . lnb . lock ) ;
dvb - > tone = tone ;
switch ( fmode ) {
default :
case 0 :
case 3 :
res = lnb_set_tone ( dev , port - > lnr , dvb - > input , tone ) ;
break ;
case 1 :
case 2 :
if ( old_quattro ) {
if ( dvb - > tone = = SEC_TONE_ON )
tuner | = 2 ;
if ( dvb - > voltage = = SEC_VOLTAGE_18 )
tuner | = 1 ;
} else {
if ( dvb - > tone = = SEC_TONE_ON )
tuner | = 1 ;
if ( dvb - > voltage = = SEC_VOLTAGE_18 )
tuner | = 2 ;
}
res = max_set_input_unlocked ( fe , tuner ) ;
break ;
}
mutex_unlock ( & dev - > link [ port - > lnr ] . lnb . lock ) ;
return res ;
}
static int max_set_voltage ( struct dvb_frontend * fe , enum fe_sec_voltage voltage )
{
struct ddb_input * input = fe - > sec_priv ;
struct ddb_port * port = input - > port ;
struct ddb * dev = port - > dev ;
struct ddb_dvb * dvb = & port - > dvb [ input - > nr & 1 ] ;
int tuner = 0 ;
u32 nv , ov = dev - > link [ port - > lnr ] . lnb . voltages ;
int res = 0 ;
u32 fmode = dev - > link [ port - > lnr ] . lnb . fmode ;
mutex_lock ( & dev - > link [ port - > lnr ] . lnb . lock ) ;
dvb - > voltage = voltage ;
switch ( fmode ) {
case 3 :
default :
case 0 :
if ( fmode = = 3 )
max_set_input_unlocked ( fe , 0 ) ;
if ( voltage = = SEC_VOLTAGE_OFF )
dev - > link [ port - > lnr ] . lnb . voltage [ dvb - > input ] & =
~ ( 1ULL < < input - > nr ) ;
else
dev - > link [ port - > lnr ] . lnb . voltage [ dvb - > input ] | =
( 1ULL < < input - > nr ) ;
res = lnb_set_voltage ( dev , port - > lnr , dvb - > input , voltage ) ;
break ;
case 1 :
case 2 :
if ( voltage = = SEC_VOLTAGE_OFF )
dev - > link [ port - > lnr ] . lnb . voltages & =
~ ( 1ULL < < input - > nr ) ;
else
dev - > link [ port - > lnr ] . lnb . voltages | =
( 1ULL < < input - > nr ) ;
nv = dev - > link [ port - > lnr ] . lnb . voltages ;
if ( old_quattro ) {
if ( dvb - > tone = = SEC_TONE_ON )
tuner | = 2 ;
if ( dvb - > voltage = = SEC_VOLTAGE_18 )
tuner | = 1 ;
} else {
if ( dvb - > tone = = SEC_TONE_ON )
tuner | = 1 ;
if ( dvb - > voltage = = SEC_VOLTAGE_18 )
tuner | = 2 ;
}
res = max_set_input_unlocked ( fe , tuner ) ;
if ( nv ! = ov ) {
if ( nv ) {
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
0 , SEC_VOLTAGE_13 ) ;
2017-07-09 15:42:44 -04:00
if ( fmode = = 1 ) {
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
0 , SEC_VOLTAGE_13 ) ;
if ( old_quattro ) {
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
1 , SEC_VOLTAGE_18 ) ;
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
2 , SEC_VOLTAGE_13 ) ;
} else {
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
1 , SEC_VOLTAGE_13 ) ;
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
2 , SEC_VOLTAGE_18 ) ;
}
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
3 , SEC_VOLTAGE_18 ) ;
}
} else {
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
0 , SEC_VOLTAGE_OFF ) ;
if ( fmode = = 1 ) {
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
1 , SEC_VOLTAGE_OFF ) ;
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
2 , SEC_VOLTAGE_OFF ) ;
2017-10-15 16:51:51 -04:00
lnb_set_voltage (
dev , port - > lnr ,
2017-07-09 15:42:44 -04:00
3 , SEC_VOLTAGE_OFF ) ;
}
}
}
break ;
}
mutex_unlock ( & dev - > link [ port - > lnr ] . lnb . lock ) ;
return res ;
}
static int max_enable_high_lnb_voltage ( struct dvb_frontend * fe , long arg )
{
return 0 ;
}
static int max_send_burst ( struct dvb_frontend * fe , enum fe_sec_mini_cmd burst )
{
return 0 ;
}
static int mxl_fw_read ( void * priv , u8 * buf , u32 len )
{
struct ddb_link * link = priv ;
struct ddb * dev = link - > dev ;
dev_info ( dev - > dev , " Read mxl_fw from link %u \n " , link - > nr ) ;
return ddbridge_flashread ( dev , link - > nr , buf , 0xc0000 , len ) ;
}
int lnb_init_fmode ( struct ddb * dev , struct ddb_link * link , u32 fm )
{
u32 l = link - > nr ;
if ( link - > lnb . fmode = = fm )
return 0 ;
dev_info ( dev - > dev , " Set fmode link %u = %u \n " , l , fm ) ;
mutex_lock ( & link - > lnb . lock ) ;
if ( fm = = 2 | | fm = = 1 ) {
if ( fmode_sat > = 0 ) {
lnb_set_sat ( dev , l , 0 , fmode_sat , 0 , 0 ) ;
if ( old_quattro ) {
lnb_set_sat ( dev , l , 1 , fmode_sat , 0 , 1 ) ;
lnb_set_sat ( dev , l , 2 , fmode_sat , 1 , 0 ) ;
} else {
lnb_set_sat ( dev , l , 1 , fmode_sat , 1 , 0 ) ;
lnb_set_sat ( dev , l , 2 , fmode_sat , 0 , 1 ) ;
}
lnb_set_sat ( dev , l , 3 , fmode_sat , 1 , 1 ) ;
}
lnb_set_tone ( dev , l , 0 , SEC_TONE_OFF ) ;
if ( old_quattro ) {
lnb_set_tone ( dev , l , 1 , SEC_TONE_OFF ) ;
lnb_set_tone ( dev , l , 2 , SEC_TONE_ON ) ;
} else {
lnb_set_tone ( dev , l , 1 , SEC_TONE_ON ) ;
lnb_set_tone ( dev , l , 2 , SEC_TONE_OFF ) ;
}
lnb_set_tone ( dev , l , 3 , SEC_TONE_ON ) ;
}
link - > lnb . fmode = fm ;
mutex_unlock ( & link - > lnb . lock ) ;
return 0 ;
}
static struct mxl5xx_cfg mxl5xx = {
. adr = 0x60 ,
. type = 0x01 ,
. clk = 27000000 ,
. ts_clk = 139 ,
. cap = 12 ,
. fw_read = mxl_fw_read ,
} ;
int fe_attach_mxl5xx ( struct ddb_input * input )
{
struct ddb * dev = input - > port - > dev ;
struct i2c_adapter * i2c = & input - > port - > i2c - > adap ;
struct ddb_dvb * dvb = & input - > port - > dvb [ input - > nr & 1 ] ;
struct ddb_port * port = input - > port ;
struct ddb_link * link = & dev - > link [ port - > lnr ] ;
struct mxl5xx_cfg cfg ;
int demod , tuner ;
cfg = mxl5xx ;
cfg . fw_priv = link ;
dvb - > set_input = NULL ;
demod = input - > nr ;
tuner = demod & 3 ;
if ( fmode = = 3 )
tuner = 0 ;
dvb - > fe = dvb_attach ( mxl5xx_attach , i2c , & cfg ,
2017-10-15 16:51:51 -04:00
demod , tuner , & dvb - > set_input ) ;
2017-07-09 15:42:44 -04:00
if ( ! dvb - > fe ) {
dev_err ( dev - > dev , " No MXL5XX found! \n " ) ;
return - ENODEV ;
}
if ( ! dvb - > set_input ) {
dev_err ( dev - > dev , " No mxl5xx_set_input function pointer! \n " ) ;
return - ENODEV ;
}
if ( input - > nr < 4 ) {
lnb_command ( dev , port - > lnr , input - > nr , LNB_CMD_INIT ) ;
lnb_set_voltage ( dev , port - > lnr , input - > nr , SEC_VOLTAGE_OFF ) ;
}
lnb_init_fmode ( dev , link , fmode ) ;
dvb - > fe - > ops . set_voltage = max_set_voltage ;
dvb - > fe - > ops . enable_high_lnb_voltage = max_enable_high_lnb_voltage ;
dvb - > fe - > ops . set_tone = max_set_tone ;
dvb - > diseqc_send_master_cmd = dvb - > fe - > ops . diseqc_send_master_cmd ;
dvb - > fe - > ops . diseqc_send_master_cmd = max_send_master_cmd ;
dvb - > fe - > ops . diseqc_send_burst = max_send_burst ;
dvb - > fe - > sec_priv = input ;
dvb - > input = tuner ;
return 0 ;
}