2014-04-14 21:55:12 -03:00
/*
* Silicon Labs Si2168 DVB - T / T2 / C demodulator driver
*
* Copyright ( C ) 2014 Antti Palosaari < crope @ iki . fi >
*
* 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 .
*/
2014-04-10 22:00:50 -03:00
# include "si2168_priv.h"
static const struct dvb_frontend_ops si2168_ops ;
2015-05-29 16:42:33 -03:00
/* Own I2C adapter locking is needed because of I2C gate logic. */
static int si2168_i2c_master_send_unlocked ( const struct i2c_client * client ,
const char * buf , int count )
{
int ret ;
struct i2c_msg msg = {
. addr = client - > addr ,
. flags = 0 ,
. len = count ,
. buf = ( char * ) buf ,
} ;
ret = __i2c_transfer ( client - > adapter , & msg , 1 ) ;
return ( ret = = 1 ) ? count : ret ;
}
static int si2168_i2c_master_recv_unlocked ( const struct i2c_client * client ,
char * buf , int count )
{
int ret ;
struct i2c_msg msg = {
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = count ,
. buf = buf ,
} ;
ret = __i2c_transfer ( client - > adapter , & msg , 1 ) ;
return ( ret = = 1 ) ? count : ret ;
}
2014-04-10 22:00:50 -03:00
/* execute firmware command */
2015-05-29 16:42:33 -03:00
static int si2168_cmd_execute_unlocked ( struct i2c_client * client ,
struct si2168_cmd * cmd )
2014-04-10 22:00:50 -03:00
{
int ret ;
unsigned long timeout ;
if ( cmd - > wlen ) {
/* write cmd and args for firmware */
2015-05-29 16:42:33 -03:00
ret = si2168_i2c_master_send_unlocked ( client , cmd - > args ,
cmd - > wlen ) ;
2014-04-10 22:00:50 -03:00
if ( ret < 0 ) {
2015-05-29 16:42:33 -03:00
goto err ;
2014-04-10 22:00:50 -03:00
} else if ( ret ! = cmd - > wlen ) {
ret = - EREMOTEIO ;
2015-05-29 16:42:33 -03:00
goto err ;
2014-04-10 22:00:50 -03:00
}
}
if ( cmd - > rlen ) {
/* wait cmd execution terminate */
2014-12-08 05:30:44 -03:00
# define TIMEOUT 70
2014-04-10 22:00:50 -03:00
timeout = jiffies + msecs_to_jiffies ( TIMEOUT ) ;
while ( ! time_after ( jiffies , timeout ) ) {
2015-05-29 16:42:33 -03:00
ret = si2168_i2c_master_recv_unlocked ( client , cmd - > args ,
cmd - > rlen ) ;
2014-04-10 22:00:50 -03:00
if ( ret < 0 ) {
2015-05-29 16:42:33 -03:00
goto err ;
2014-04-10 22:00:50 -03:00
} else if ( ret ! = cmd - > rlen ) {
ret = - EREMOTEIO ;
2015-05-29 16:42:33 -03:00
goto err ;
2014-04-10 22:00:50 -03:00
}
/* firmware ready? */
if ( ( cmd - > args [ 0 ] > > 7 ) & 0x01 )
break ;
}
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev , " cmd execution took %d ms \n " ,
2014-04-10 22:00:50 -03:00
jiffies_to_msecs ( jiffies ) -
( jiffies_to_msecs ( timeout ) - TIMEOUT ) ) ;
2015-05-05 13:54:16 -03:00
/* error bit set? */
if ( ( cmd - > args [ 0 ] > > 6 ) & 0x01 ) {
ret = - EREMOTEIO ;
2015-05-29 16:42:33 -03:00
goto err ;
2015-05-05 13:54:16 -03:00
}
2014-06-13 11:08:25 -03:00
if ( ! ( ( cmd - > args [ 0 ] > > 7 ) & 0x01 ) ) {
2014-04-10 22:00:50 -03:00
ret = - ETIMEDOUT ;
2015-05-29 16:42:33 -03:00
goto err ;
2014-04-10 22:00:50 -03:00
}
}
2014-12-05 14:30:44 -03:00
return 0 ;
2015-05-29 16:42:33 -03:00
err :
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 22:00:50 -03:00
return ret ;
}
2015-05-29 16:42:33 -03:00
static int si2168_cmd_execute ( struct i2c_client * client , struct si2168_cmd * cmd )
{
int ret ;
i2c_lock_adapter ( client - > adapter ) ;
ret = si2168_cmd_execute_unlocked ( client , cmd ) ;
i2c_unlock_adapter ( client - > adapter ) ;
return ret ;
}
2015-06-07 14:53:52 -03:00
static int si2168_read_status ( struct dvb_frontend * fe , enum fe_status * status )
2014-04-10 22:00:50 -03:00
{
2014-11-25 17:31:43 -03:00
struct i2c_client * client = fe - > demodulator_priv ;
struct si2168_dev * dev = i2c_get_clientdata ( client ) ;
2014-04-11 20:35:27 -03:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2014-04-10 22:00:50 -03:00
int ret ;
struct si2168_cmd cmd ;
* status = 0 ;
2014-11-25 16:46:16 -03:00
if ( ! dev - > active ) {
2014-04-10 22:00:50 -03:00
ret = - EAGAIN ;
goto err ;
}
2014-04-11 20:35:27 -03:00
switch ( c - > delivery_system ) {
case SYS_DVBT :
2014-07-09 19:36:58 -03:00
memcpy ( cmd . args , " \xa0 \x01 " , 2 ) ;
2014-04-11 20:35:27 -03:00
cmd . wlen = 2 ;
cmd . rlen = 13 ;
break ;
2014-04-12 01:58:32 -03:00
case SYS_DVBC_ANNEX_A :
2014-07-09 19:36:58 -03:00
memcpy ( cmd . args , " \x90 \x01 " , 2 ) ;
2014-04-12 01:58:32 -03:00
cmd . wlen = 2 ;
cmd . rlen = 9 ;
break ;
2014-04-11 20:35:27 -03:00
case SYS_DVBT2 :
2014-07-09 19:36:58 -03:00
memcpy ( cmd . args , " \x50 \x01 " , 2 ) ;
2014-04-11 20:35:27 -03:00
cmd . wlen = 2 ;
cmd . rlen = 14 ;
break ;
default :
ret = - EINVAL ;
goto err ;
}
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-04-22 21:36:32 -03:00
switch ( ( cmd . args [ 2 ] > > 1 ) & 0x03 ) {
case 0x01 :
2014-04-10 22:00:50 -03:00
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER ;
break ;
2014-04-22 21:36:32 -03:00
case 0x03 :
2014-04-10 22:00:50 -03:00
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
FE_HAS_SYNC | FE_HAS_LOCK ;
break ;
}
2014-11-25 16:46:16 -03:00
dev - > fe_status = * status ;
2014-04-10 22:00:50 -03:00
2014-07-09 20:22:27 -03:00
if ( * status & FE_HAS_LOCK ) {
c - > cnr . len = 1 ;
c - > cnr . stat [ 0 ] . scale = FE_SCALE_DECIBEL ;
c - > cnr . stat [ 0 ] . svalue = cmd . args [ 3 ] * 1000 / 4 ;
} else {
c - > cnr . len = 1 ;
c - > cnr . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
}
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev , " status=%02x args=%*ph \n " ,
2014-08-05 08:54:08 -03:00
* status , cmd . rlen , cmd . args ) ;
2014-04-10 22:00:50 -03:00
return 0 ;
err :
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 22:00:50 -03:00
return ret ;
}
static int si2168_set_frontend ( struct dvb_frontend * fe )
{
2014-11-25 17:31:43 -03:00
struct i2c_client * client = fe - > demodulator_priv ;
struct si2168_dev * dev = i2c_get_clientdata ( client ) ;
2014-04-10 22:00:50 -03:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
int ret ;
struct si2168_cmd cmd ;
2014-04-11 20:35:27 -03:00
u8 bandwidth , delivery_system ;
2014-04-10 22:00:50 -03:00
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev ,
2014-12-05 15:57:03 -03:00
" delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u stream_id=%u \n " ,
c - > delivery_system , c - > modulation , c - > frequency ,
c - > bandwidth_hz , c - > symbol_rate , c - > inversion ,
c - > stream_id ) ;
2014-04-10 22:00:50 -03:00
2014-11-25 16:46:16 -03:00
if ( ! dev - > active ) {
2014-04-10 22:00:50 -03:00
ret = - EAGAIN ;
goto err ;
}
2014-04-11 20:35:27 -03:00
switch ( c - > delivery_system ) {
case SYS_DVBT :
delivery_system = 0x20 ;
break ;
2014-04-12 01:58:32 -03:00
case SYS_DVBC_ANNEX_A :
delivery_system = 0x30 ;
break ;
2014-04-11 20:35:27 -03:00
case SYS_DVBT2 :
delivery_system = 0x70 ;
break ;
default :
ret = - EINVAL ;
goto err ;
}
2015-01-16 09:35:19 -03:00
if ( c - > bandwidth_hz = = 0 ) {
ret = - EINVAL ;
goto err ;
2015-01-16 09:35:20 -03:00
} else if ( c - > bandwidth_hz < = 2000000 )
bandwidth = 0x02 ;
else if ( c - > bandwidth_hz < = 5000000 )
2014-04-11 20:35:27 -03:00
bandwidth = 0x05 ;
2014-04-12 01:58:32 -03:00
else if ( c - > bandwidth_hz < = 6000000 )
2014-04-11 20:35:27 -03:00
bandwidth = 0x06 ;
2014-04-12 01:58:32 -03:00
else if ( c - > bandwidth_hz < = 7000000 )
2014-04-11 20:35:27 -03:00
bandwidth = 0x07 ;
2014-04-12 01:58:32 -03:00
else if ( c - > bandwidth_hz < = 8000000 )
2014-04-11 20:35:27 -03:00
bandwidth = 0x08 ;
2014-04-12 01:58:32 -03:00
else if ( c - > bandwidth_hz < = 9000000 )
bandwidth = 0x09 ;
else if ( c - > bandwidth_hz < = 10000000 )
bandwidth = 0x0a ;
else
bandwidth = 0x0f ;
2014-04-10 22:00:50 -03:00
/* program tuner */
if ( fe - > ops . tuner_ops . set_params ) {
ret = fe - > ops . tuner_ops . set_params ( fe ) ;
if ( ret )
goto err ;
}
memcpy ( cmd . args , " \x88 \x02 \x02 \x02 \x02 " , 5 ) ;
cmd . wlen = 5 ;
cmd . rlen = 5 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-04-11 20:35:27 -03:00
/* that has no big effect */
if ( c - > delivery_system = = SYS_DVBT )
memcpy ( cmd . args , " \x89 \x21 \x06 \x11 \xff \x98 " , 6 ) ;
2014-04-12 01:58:32 -03:00
else if ( c - > delivery_system = = SYS_DVBC_ANNEX_A )
memcpy ( cmd . args , " \x89 \x21 \x06 \x11 \x89 \xf0 " , 6 ) ;
2014-04-11 20:35:27 -03:00
else if ( c - > delivery_system = = SYS_DVBT2 )
memcpy ( cmd . args , " \x89 \x21 \x06 \x11 \x89 \x20 " , 6 ) ;
2014-04-10 22:00:50 -03:00
cmd . wlen = 6 ;
cmd . rlen = 3 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-08-16 18:33:14 -03:00
if ( c - > delivery_system = = SYS_DVBT2 ) {
/* select PLP */
cmd . args [ 0 ] = 0x52 ;
cmd . args [ 1 ] = c - > stream_id & 0xff ;
cmd . args [ 2 ] = c - > stream_id = = NO_STREAM_ID_FILTER ? 0 : 1 ;
cmd . wlen = 3 ;
cmd . rlen = 1 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-08-16 18:33:14 -03:00
if ( ret )
goto err ;
}
2014-04-10 22:00:50 -03:00
memcpy ( cmd . args , " \x51 \x03 " , 2 ) ;
cmd . wlen = 2 ;
cmd . rlen = 12 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
memcpy ( cmd . args , " \x12 \x08 \x04 " , 3 ) ;
cmd . wlen = 3 ;
cmd . rlen = 3 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
memcpy ( cmd . args , " \x14 \x00 \x0c \x10 \x12 \x00 " , 6 ) ;
cmd . wlen = 6 ;
2014-07-11 10:40:29 -03:00
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
memcpy ( cmd . args , " \x14 \x00 \x06 \x10 \x24 \x00 " , 6 ) ;
cmd . wlen = 6 ;
2014-07-11 10:40:29 -03:00
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
memcpy ( cmd . args , " \x14 \x00 \x07 \x10 \x00 \x24 " , 6 ) ;
cmd . wlen = 6 ;
2014-07-11 10:40:29 -03:00
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
memcpy ( cmd . args , " \x14 \x00 \x0a \x10 \x00 \x00 " , 6 ) ;
2014-04-11 20:35:27 -03:00
cmd . args [ 4 ] = delivery_system | bandwidth ;
2014-04-10 22:00:50 -03:00
cmd . wlen = 6 ;
2014-07-11 10:40:29 -03:00
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-07-17 14:31:28 -03:00
/* set DVB-C symbol rate */
if ( c - > delivery_system = = SYS_DVBC_ANNEX_A ) {
memcpy ( cmd . args , " \x14 \x00 \x02 \x11 " , 4 ) ;
2014-12-05 14:54:14 -03:00
cmd . args [ 4 ] = ( ( c - > symbol_rate / 1000 ) > > 0 ) & 0xff ;
2014-07-17 14:31:28 -03:00
cmd . args [ 5 ] = ( ( c - > symbol_rate / 1000 ) > > 8 ) & 0xff ;
cmd . wlen = 6 ;
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-07-17 14:31:28 -03:00
if ( ret )
goto err ;
}
2014-04-10 22:00:50 -03:00
memcpy ( cmd . args , " \x14 \x00 \x0f \x10 \x10 \x00 " , 6 ) ;
cmd . wlen = 6 ;
2014-07-11 10:40:29 -03:00
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-11-14 18:22:10 -03:00
memcpy ( cmd . args , " \x14 \x00 \x09 \x10 \xe3 \x08 " , 6 ) ;
2014-11-25 16:46:16 -03:00
cmd . args [ 5 ] | = dev - > ts_clock_inv ? 0x00 : 0x10 ;
2014-04-10 22:00:50 -03:00
cmd . wlen = 6 ;
2014-07-11 10:40:29 -03:00
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-11-14 18:22:10 -03:00
memcpy ( cmd . args , " \x14 \x00 \x08 \x10 \xd7 \x05 " , 6 ) ;
2014-11-25 16:46:16 -03:00
cmd . args [ 5 ] | = dev - > ts_clock_inv ? 0x00 : 0x10 ;
2014-04-10 22:00:50 -03:00
cmd . wlen = 6 ;
2014-07-11 10:40:29 -03:00
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
memcpy ( cmd . args , " \x14 \x00 \x01 \x12 \x00 \x00 " , 6 ) ;
cmd . wlen = 6 ;
2014-07-11 10:40:29 -03:00
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-07-17 15:43:27 -03:00
memcpy ( cmd . args , " \x14 \x00 \x01 \x03 \x0c \x00 " , 6 ) ;
cmd . wlen = 6 ;
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-07-17 15:43:27 -03:00
if ( ret )
goto err ;
2014-07-09 19:36:58 -03:00
memcpy ( cmd . args , " \x85 " , 1 ) ;
2014-04-10 22:00:50 -03:00
cmd . wlen = 1 ;
cmd . rlen = 1 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-11-25 16:46:16 -03:00
dev - > delivery_system = c - > delivery_system ;
2014-04-10 22:00:50 -03:00
return 0 ;
err :
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 22:00:50 -03:00
return ret ;
}
static int si2168_init ( struct dvb_frontend * fe )
{
2014-11-25 17:31:43 -03:00
struct i2c_client * client = fe - > demodulator_priv ;
struct si2168_dev * dev = i2c_get_clientdata ( client ) ;
2014-04-10 22:00:50 -03:00
int ret , len , remaining ;
2014-12-05 17:22:42 -03:00
const struct firmware * fw ;
2014-12-06 17:53:16 -03:00
const char * fw_name ;
2014-04-10 22:00:50 -03:00
struct si2168_cmd cmd ;
2014-07-13 19:22:19 -03:00
unsigned int chip_id ;
2014-04-10 22:00:50 -03:00
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-04-10 22:00:50 -03:00
2014-08-25 15:07:04 -03:00
/* initialize */
2014-07-09 19:36:58 -03:00
memcpy ( cmd . args , " \xc0 \x12 \x00 \x0c \x00 \x0d \x16 \x00 \x00 \x00 \x00 \x00 \x00 " , 13 ) ;
2014-04-10 22:00:50 -03:00
cmd . wlen = 13 ;
cmd . rlen = 0 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-11-25 16:46:16 -03:00
if ( dev - > fw_loaded ) {
2014-08-25 15:07:04 -03:00
/* resume */
memcpy ( cmd . args , " \xc0 \x06 \x08 \x0f \x00 \x20 \x21 \x01 " , 8 ) ;
cmd . wlen = 8 ;
cmd . rlen = 1 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-08-25 15:07:04 -03:00
if ( ret )
goto err ;
memcpy ( cmd . args , " \x85 " , 1 ) ;
cmd . wlen = 1 ;
cmd . rlen = 1 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-08-25 15:07:04 -03:00
if ( ret )
goto err ;
goto warm ;
}
/* power up */
2014-07-09 19:36:58 -03:00
memcpy ( cmd . args , " \xc0 \x06 \x01 \x0f \x00 \x20 \x20 \x01 " , 8 ) ;
2014-04-10 22:00:50 -03:00
cmd . wlen = 8 ;
cmd . rlen = 1 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-07-13 10:52:18 -03:00
/* query chip revision */
2014-07-09 19:36:58 -03:00
memcpy ( cmd . args , " \x02 " , 1 ) ;
2014-04-10 22:00:50 -03:00
cmd . wlen = 1 ;
cmd . rlen = 13 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-07-13 19:22:19 -03:00
chip_id = cmd . args [ 1 ] < < 24 | cmd . args [ 2 ] < < 16 | cmd . args [ 3 ] < < 8 |
cmd . args [ 4 ] < < 0 ;
2014-07-17 19:43:37 -03:00
# define SI2168_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0)
2014-07-13 19:22:19 -03:00
# define SI2168_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0)
# define SI2168_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0)
switch ( chip_id ) {
2014-07-17 19:43:37 -03:00
case SI2168_A20 :
2014-12-06 17:53:16 -03:00
fw_name = SI2168_A20_FIRMWARE ;
2014-07-17 19:43:37 -03:00
break ;
2014-07-13 19:22:19 -03:00
case SI2168_A30 :
2014-12-06 17:53:16 -03:00
fw_name = SI2168_A30_FIRMWARE ;
2014-07-13 19:22:19 -03:00
break ;
case SI2168_B40 :
2014-12-06 17:53:16 -03:00
fw_name = SI2168_B40_FIRMWARE ;
2014-07-13 19:22:19 -03:00
break ;
default :
2014-12-06 17:43:27 -03:00
dev_err ( & client - > dev , " unknown chip version Si21%d-%c%c%c \n " ,
2014-08-05 08:54:08 -03:00
cmd . args [ 2 ] , cmd . args [ 1 ] ,
2014-07-13 19:22:19 -03:00
cmd . args [ 3 ] , cmd . args [ 4 ] ) ;
2014-07-13 10:52:18 -03:00
ret = - EINVAL ;
goto err ;
}
2014-12-06 17:43:27 -03:00
dev_info ( & client - > dev , " found a 'Silicon Labs Si21%d-%c%c%c' \n " ,
cmd . args [ 2 ] , cmd . args [ 1 ] , cmd . args [ 3 ] , cmd . args [ 4 ] ) ;
2014-04-10 22:00:50 -03:00
/* request the firmware, this will block and timeout */
2014-12-06 17:53:16 -03:00
ret = request_firmware ( & fw , fw_name , & client - > dev ) ;
2014-04-10 22:00:50 -03:00
if ( ret ) {
2014-07-13 19:22:19 -03:00
/* fallback mechanism to handle old name for Si2168 B40 fw */
if ( chip_id = = SI2168_B40 ) {
2014-12-06 17:53:16 -03:00
fw_name = SI2168_B40_FIRMWARE_FALLBACK ;
ret = request_firmware ( & fw , fw_name , & client - > dev ) ;
2014-07-13 10:52:18 -03:00
}
2014-07-13 19:22:19 -03:00
if ( ret = = 0 ) {
2014-11-25 17:53:21 -03:00
dev_notice ( & client - > dev ,
2014-08-05 08:54:08 -03:00
" please install firmware file '%s' \n " ,
SI2168_B40_FIRMWARE ) ;
2014-07-13 19:22:19 -03:00
} else {
2014-11-25 17:53:21 -03:00
dev_err ( & client - > dev ,
2014-08-05 08:54:08 -03:00
" firmware file '%s' not found \n " ,
2014-12-06 17:53:16 -03:00
fw_name ) ;
2014-12-05 14:54:14 -03:00
goto err_release_firmware ;
2014-07-13 10:52:18 -03:00
}
2014-04-10 22:00:50 -03:00
}
2014-11-25 17:53:21 -03:00
dev_info ( & client - > dev , " downloading firmware from file '%s' \n " ,
2014-12-06 17:53:16 -03:00
fw_name ) ;
2014-04-10 22:00:50 -03:00
2014-11-27 16:42:23 -03:00
if ( ( fw - > size % 17 = = 0 ) & & ( fw - > data [ 0 ] > 5 ) ) {
/* firmware is in the new format */
for ( remaining = fw - > size ; remaining > 0 ; remaining - = 17 ) {
len = fw - > data [ fw - > size - remaining ] ;
2015-09-29 21:10:09 -03:00
if ( len > SI2168_ARGLEN ) {
ret = - EINVAL ;
break ;
}
2014-11-27 16:42:23 -03:00
memcpy ( cmd . args , & fw - > data [ ( fw - > size - remaining ) + 1 ] , len ) ;
cmd . wlen = len ;
cmd . rlen = 1 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-12-05 17:08:22 -03:00
if ( ret )
break ;
2014-11-27 16:42:23 -03:00
}
2014-12-05 17:08:22 -03:00
} else if ( fw - > size % 8 = = 0 ) {
2014-11-27 16:42:23 -03:00
/* firmware is in the old format */
2014-12-05 17:08:22 -03:00
for ( remaining = fw - > size ; remaining > 0 ; remaining - = 8 ) {
len = 8 ;
2014-11-27 16:42:23 -03:00
memcpy ( cmd . args , & fw - > data [ fw - > size - remaining ] , len ) ;
cmd . wlen = len ;
cmd . rlen = 1 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-12-05 17:08:22 -03:00
if ( ret )
break ;
2014-04-10 22:00:50 -03:00
}
2014-12-05 17:08:22 -03:00
} else {
/* bad or unknown firmware format */
ret = - EINVAL ;
}
if ( ret ) {
dev_err ( & client - > dev , " firmware download failed %d \n " , ret ) ;
goto err_release_firmware ;
2014-04-10 22:00:50 -03:00
}
release_firmware ( fw ) ;
2014-07-09 19:36:58 -03:00
memcpy ( cmd . args , " \x01 \x01 " , 2 ) ;
2014-04-10 22:00:50 -03:00
cmd . wlen = 2 ;
cmd . rlen = 1 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-04-10 22:00:50 -03:00
if ( ret )
goto err ;
2014-11-27 16:42:22 -03:00
/* query firmware version */
memcpy ( cmd . args , " \x11 " , 1 ) ;
cmd . wlen = 1 ;
cmd . rlen = 10 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-11-27 16:42:22 -03:00
if ( ret )
goto err ;
2014-12-05 15:04:12 -03:00
dev_info ( & client - > dev , " firmware version: %c.%c.%d \n " ,
2014-11-27 16:42:22 -03:00
cmd . args [ 6 ] , cmd . args [ 7 ] , cmd . args [ 8 ] ) ;
2014-08-11 16:58:10 -03:00
/* set ts mode */
memcpy ( cmd . args , " \x14 \x00 \x01 \x10 \x10 \x00 " , 6 ) ;
2014-11-25 16:46:16 -03:00
cmd . args [ 4 ] | = dev - > ts_mode ;
2015-05-05 13:54:14 -03:00
if ( dev - > ts_clock_gapped )
cmd . args [ 4 ] | = 0x40 ;
2014-08-11 16:58:10 -03:00
cmd . wlen = 6 ;
cmd . rlen = 4 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-08-11 16:58:10 -03:00
if ( ret )
goto err ;
2014-11-25 16:46:16 -03:00
dev - > fw_loaded = true ;
2014-11-03 18:28:39 -03:00
warm :
2014-11-25 16:46:16 -03:00
dev - > active = true ;
2014-04-10 22:00:50 -03:00
return 0 ;
2014-12-06 17:43:27 -03:00
2014-12-05 14:54:14 -03:00
err_release_firmware :
2014-11-19 19:23:15 -03:00
release_firmware ( fw ) ;
err :
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 22:00:50 -03:00
return ret ;
}
static int si2168_sleep ( struct dvb_frontend * fe )
{
2014-11-25 17:31:43 -03:00
struct i2c_client * client = fe - > demodulator_priv ;
struct si2168_dev * dev = i2c_get_clientdata ( client ) ;
2014-07-09 18:45:31 -03:00
int ret ;
struct si2168_cmd cmd ;
2014-04-10 22:00:50 -03:00
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-04-10 22:00:50 -03:00
2014-11-25 16:46:16 -03:00
dev - > active = false ;
2014-04-10 22:00:50 -03:00
2014-07-09 18:45:31 -03:00
memcpy ( cmd . args , " \x13 " , 1 ) ;
cmd . wlen = 1 ;
cmd . rlen = 0 ;
2014-11-25 17:31:43 -03:00
ret = si2168_cmd_execute ( client , & cmd ) ;
2014-07-09 18:45:31 -03:00
if ( ret )
goto err ;
2014-04-10 22:00:50 -03:00
return 0 ;
2014-07-09 18:45:31 -03:00
err :
2014-11-25 17:53:21 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-07-09 18:45:31 -03:00
return ret ;
2014-04-10 22:00:50 -03:00
}
static int si2168_get_tune_settings ( struct dvb_frontend * fe ,
struct dvb_frontend_tune_settings * s )
{
s - > min_delay_ms = 900 ;
return 0 ;
}
/*
* I2C gate logic
2015-05-29 16:42:33 -03:00
* We must use unlocked I2C I / O because I2C adapter lock is already taken
* by the caller ( usually tuner driver ) .
2014-04-10 22:00:50 -03:00
*/
2016-04-20 08:41:36 +02:00
static int si2168_select ( struct i2c_mux_core * muxc , u32 chan )
2014-04-10 22:00:50 -03:00
{
2016-04-20 08:41:36 +02:00
struct i2c_client * client = i2c_mux_priv ( muxc ) ;
2014-04-10 22:00:50 -03:00
int ret ;
2015-05-29 16:42:33 -03:00
struct si2168_cmd cmd ;
2014-04-10 22:00:50 -03:00
2015-05-29 16:42:33 -03:00
/* open I2C gate */
memcpy ( cmd . args , " \xc0 \x0d \x01 " , 3 ) ;
cmd . wlen = 3 ;
cmd . rlen = 0 ;
ret = si2168_cmd_execute_unlocked ( client , & cmd ) ;
if ( ret )
goto err ;
2014-04-10 22:00:50 -03:00
2015-05-29 16:42:33 -03:00
return 0 ;
err :
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 22:00:50 -03:00
return ret ;
}
2016-04-20 08:41:36 +02:00
static int si2168_deselect ( struct i2c_mux_core * muxc , u32 chan )
2014-04-10 22:00:50 -03:00
{
2016-04-20 08:41:36 +02:00
struct i2c_client * client = i2c_mux_priv ( muxc ) ;
2014-04-10 22:00:50 -03:00
int ret ;
2015-05-29 16:42:33 -03:00
struct si2168_cmd cmd ;
2014-04-10 22:00:50 -03:00
2015-05-29 16:42:33 -03:00
/* close I2C gate */
memcpy ( cmd . args , " \xc0 \x0d \x00 " , 3 ) ;
cmd . wlen = 3 ;
cmd . rlen = 0 ;
ret = si2168_cmd_execute_unlocked ( client , & cmd ) ;
if ( ret )
goto err ;
2014-04-10 22:00:50 -03:00
2015-05-29 16:42:33 -03:00
return 0 ;
err :
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 22:00:50 -03:00
return ret ;
}
static const struct dvb_frontend_ops si2168_ops = {
2014-04-12 01:58:32 -03:00
. delsys = { SYS_DVBT , SYS_DVBT2 , SYS_DVBC_ANNEX_A } ,
2014-04-10 22:00:50 -03:00
. info = {
. name = " Silicon Labs Si2168 " ,
2014-11-25 16:26:49 -03:00
. symbol_rate_min = 1000000 ,
. symbol_rate_max = 7200000 ,
2014-04-10 22:00:50 -03:00
. caps = FE_CAN_FEC_1_2 |
FE_CAN_FEC_2_3 |
FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 |
FE_CAN_FEC_7_8 |
FE_CAN_FEC_AUTO |
FE_CAN_QPSK |
FE_CAN_QAM_16 |
FE_CAN_QAM_32 |
FE_CAN_QAM_64 |
FE_CAN_QAM_128 |
FE_CAN_QAM_256 |
FE_CAN_QAM_AUTO |
FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_HIERARCHY_AUTO |
FE_CAN_MUTE_TS |
2014-09-23 13:53:09 -03:00
FE_CAN_2G_MODULATION |
FE_CAN_MULTISTREAM
2014-04-10 22:00:50 -03:00
} ,
. get_tune_settings = si2168_get_tune_settings ,
. init = si2168_init ,
. sleep = si2168_sleep ,
. set_frontend = si2168_set_frontend ,
. read_status = si2168_read_status ,
} ;
static int si2168_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct si2168_config * config = client - > dev . platform_data ;
2014-11-25 16:46:16 -03:00
struct si2168_dev * dev ;
2014-04-10 22:00:50 -03:00
int ret ;
2014-08-05 08:54:08 -03:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-04-10 22:00:50 -03:00
2014-11-25 16:46:16 -03:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
2014-04-10 22:00:50 -03:00
ret = - ENOMEM ;
2014-08-05 08:54:08 -03:00
dev_err ( & client - > dev , " kzalloc() failed \n " ) ;
2014-12-05 16:02:42 -03:00
goto err ;
2014-04-10 22:00:50 -03:00
}
/* create mux i2c adapter for tuner */
2016-04-20 08:41:36 +02:00
dev - > muxc = i2c_mux_alloc ( client - > adapter , & client - > dev ,
1 , 0 , 0 ,
si2168_select , si2168_deselect ) ;
if ( ! dev - > muxc ) {
ret = - ENOMEM ;
2014-12-05 14:54:14 -03:00
goto err_kfree ;
2014-07-17 16:38:08 -03:00
}
2016-04-20 08:41:36 +02:00
dev - > muxc - > priv = client ;
ret = i2c_mux_add_adapter ( dev - > muxc , 0 , 0 , 0 ) ;
if ( ret )
goto err_kfree ;
2014-04-10 22:00:50 -03:00
/* create dvb_frontend */
2014-11-25 16:46:16 -03:00
memcpy ( & dev - > fe . ops , & si2168_ops , sizeof ( struct dvb_frontend_ops ) ) ;
2014-11-25 17:31:43 -03:00
dev - > fe . demodulator_priv = client ;
2016-04-20 08:41:36 +02:00
* config - > i2c_adapter = dev - > muxc - > adapter [ 0 ] ;
2014-11-25 16:46:16 -03:00
* config - > fe = & dev - > fe ;
dev - > ts_mode = config - > ts_mode ;
dev - > ts_clock_inv = config - > ts_clock_inv ;
2015-05-05 13:54:14 -03:00
dev - > ts_clock_gapped = config - > ts_clock_gapped ;
2014-11-25 16:46:16 -03:00
dev - > fw_loaded = false ;
2014-04-10 22:00:50 -03:00
2014-11-25 16:46:16 -03:00
i2c_set_clientdata ( client , dev ) ;
2014-04-10 22:00:50 -03:00
2014-12-05 14:54:14 -03:00
dev_info ( & client - > dev , " Silicon Labs Si2168 successfully attached \n " ) ;
2014-04-10 22:00:50 -03:00
return 0 ;
2014-12-05 14:54:14 -03:00
err_kfree :
2014-11-25 16:46:16 -03:00
kfree ( dev ) ;
2014-12-05 16:02:42 -03:00
err :
2014-08-05 08:54:08 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 22:00:50 -03:00
return ret ;
}
static int si2168_remove ( struct i2c_client * client )
{
2014-11-25 16:46:16 -03:00
struct si2168_dev * dev = i2c_get_clientdata ( client ) ;
2014-04-10 22:00:50 -03:00
2014-08-05 08:54:08 -03:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-04-10 22:00:50 -03:00
2016-04-20 08:41:36 +02:00
i2c_mux_del_adapters ( dev - > muxc ) ;
2014-04-10 22:00:50 -03:00
2014-11-25 16:46:16 -03:00
dev - > fe . ops . release = NULL ;
dev - > fe . demodulator_priv = NULL ;
2014-04-10 22:00:50 -03:00
2014-11-25 16:46:16 -03:00
kfree ( dev ) ;
2014-04-10 22:00:50 -03:00
return 0 ;
}
2014-12-05 14:54:14 -03:00
static const struct i2c_device_id si2168_id_table [ ] = {
2014-04-10 22:00:50 -03:00
{ " si2168 " , 0 } ,
{ }
} ;
2014-12-05 14:54:14 -03:00
MODULE_DEVICE_TABLE ( i2c , si2168_id_table ) ;
2014-04-10 22:00:50 -03:00
static struct i2c_driver si2168_driver = {
. driver = {
. name = " si2168 " ,
} ,
. probe = si2168_probe ,
. remove = si2168_remove ,
2014-12-05 14:54:14 -03:00
. id_table = si2168_id_table ,
2014-04-10 22:00:50 -03:00
} ;
module_i2c_driver ( si2168_driver ) ;
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_DESCRIPTION ( " Silicon Labs Si2168 DVB-T/T2/C demodulator driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2014-07-17 19:43:37 -03:00
MODULE_FIRMWARE ( SI2168_A20_FIRMWARE ) ;
2014-07-13 16:09:03 -03:00
MODULE_FIRMWARE ( SI2168_A30_FIRMWARE ) ;
2014-07-13 10:52:18 -03:00
MODULE_FIRMWARE ( SI2168_B40_FIRMWARE ) ;