2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-04-14 21:51:32 -03:00
/*
2017-04-06 09:17:42 -03:00
* Silicon Labs Si2146 / 2147 / 2148 / 2157 / 2158 silicon tuner driver
2014-04-14 21:51:32 -03:00
*
* Copyright ( C ) 2014 Antti Palosaari < crope @ iki . fi >
*/
2014-04-10 21:58:10 -03:00
# include "si2157_priv.h"
2014-07-13 10:52:20 -03:00
static const struct dvb_tuner_ops si2157_ops ;
2019-11-14 21:04:00 +01:00
static int tuner_lock_debug ;
module_param ( tuner_lock_debug , int , 0644 ) ;
MODULE_PARM_DESC ( tuner_lock_debug , " if set, signal lock is briefly waited on after setting params " ) ;
2014-04-10 21:58:10 -03:00
/* execute firmware command */
2014-12-06 14:40:06 -03:00
static int si2157_cmd_execute ( struct i2c_client * client , struct si2157_cmd * cmd )
2014-04-10 21:58:10 -03:00
{
2014-12-06 14:40:06 -03:00
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
2014-04-10 21:58:10 -03:00
int ret ;
unsigned long timeout ;
2014-12-05 17:46:30 -03:00
mutex_lock ( & dev - > i2c_mutex ) ;
2014-04-10 21:58:10 -03:00
2014-07-10 06:02:53 -03:00
if ( cmd - > wlen ) {
2014-04-10 21:58:10 -03:00
/* write cmd and args for firmware */
2014-12-06 14:40:06 -03:00
ret = i2c_master_send ( client , cmd - > args , cmd - > wlen ) ;
2014-04-10 21:58:10 -03:00
if ( ret < 0 ) {
goto err_mutex_unlock ;
2014-07-10 06:02:53 -03:00
} else if ( ret ! = cmd - > wlen ) {
2014-04-10 21:58:10 -03:00
ret = - EREMOTEIO ;
goto err_mutex_unlock ;
}
}
2014-07-10 06:02:53 -03:00
if ( cmd - > rlen ) {
/* wait cmd execution terminate */
# define TIMEOUT 80
timeout = jiffies + msecs_to_jiffies ( TIMEOUT ) ;
while ( ! time_after ( jiffies , timeout ) ) {
2014-12-06 14:40:06 -03:00
ret = i2c_master_recv ( client , cmd - > args , cmd - > rlen ) ;
2014-07-10 06:02:53 -03:00
if ( ret < 0 ) {
goto err_mutex_unlock ;
} else if ( ret ! = cmd - > rlen ) {
ret = - EREMOTEIO ;
goto err_mutex_unlock ;
}
/* firmware ready? */
if ( ( cmd - > args [ 0 ] > > 7 ) & 0x01 )
break ;
2014-04-10 21:58:10 -03:00
}
2019-11-14 21:03:56 +01:00
dev_dbg ( & client - > dev , " cmd execution took %d ms, status=%x \n " ,
jiffies_to_msecs ( jiffies ) -
( jiffies_to_msecs ( timeout ) - TIMEOUT ) ,
cmd - > args [ 0 ] ) ;
2014-04-10 21:58:10 -03:00
2014-07-10 06:02:53 -03:00
if ( ! ( ( cmd - > args [ 0 ] > > 7 ) & 0x01 ) ) {
ret = - ETIMEDOUT ;
goto err_mutex_unlock ;
}
2019-11-14 21:03:56 +01:00
/* check error status bit */
if ( cmd - > args [ 0 ] & 0x40 ) {
ret = - EAGAIN ;
goto err_mutex_unlock ;
}
2014-04-10 21:58:10 -03:00
}
2014-12-06 14:04:05 -03:00
mutex_unlock ( & dev - > i2c_mutex ) ;
return 0 ;
2014-07-10 06:02:53 -03:00
2014-04-10 21:58:10 -03:00
err_mutex_unlock :
2014-12-05 17:46:30 -03:00
mutex_unlock ( & dev - > i2c_mutex ) ;
2014-12-06 14:40:06 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 21:58:10 -03:00
return ret ;
}
static int si2157_init ( struct dvb_frontend * fe )
{
2014-12-06 14:40:06 -03:00
struct i2c_client * client = fe - > tuner_priv ;
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
2014-09-07 11:20:34 -03:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2014-07-13 20:05:28 -03:00
int ret , len , remaining ;
2014-07-13 10:52:19 -03:00
struct si2157_cmd cmd ;
2014-12-06 14:51:17 -03:00
const struct firmware * fw ;
2014-12-06 18:15:18 -03:00
const char * fw_name ;
2019-11-14 21:03:57 +01:00
unsigned int chip_id , xtal_trim ;
2014-04-10 21:58:10 -03:00
2014-12-06 14:40:06 -03:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-04-10 21:58:10 -03:00
2019-11-14 21:03:57 +01:00
/* Try to get Xtal trim property, to verify tuner still running */
memcpy ( cmd . args , " \x15 \x00 \x04 \x02 " , 4 ) ;
2016-02-08 09:51:16 -02:00
cmd . wlen = 4 ;
cmd . rlen = 4 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
2019-11-14 21:03:57 +01:00
xtal_trim = cmd . args [ 2 ] | ( cmd . args [ 3 ] < < 8 ) ;
2016-02-08 09:51:16 -02:00
2019-11-14 21:03:57 +01:00
if ( ret = = 0 & & xtal_trim < 16 )
2014-08-25 15:07:03 -03:00
goto warm ;
2019-11-14 21:03:57 +01:00
dev - > if_frequency = 0 ; /* we no longer know current tuner state */
2014-08-25 15:07:03 -03:00
/* power up */
2017-04-06 09:17:42 -03:00
if ( dev - > chiptype = = SI2157_CHIPTYPE_SI2146 ) {
2014-11-24 03:57:33 -03:00
memcpy ( cmd . args , " \xc0 \x05 \x01 \x00 \x00 \x0b \x00 \x00 \x01 " , 9 ) ;
cmd . wlen = 9 ;
2017-02-16 22:55:31 -02:00
} else if ( dev - > chiptype = = SI2157_CHIPTYPE_SI2141 ) {
memcpy ( cmd . args , " \xc0 \x00 \x0d \x0e \x00 \x01 \x01 \x01 \x01 \x03 " , 10 ) ;
cmd . wlen = 10 ;
2017-04-06 09:17:42 -03:00
} else {
2014-11-24 03:57:33 -03:00
memcpy ( cmd . args , " \xc0 \x00 \x0c \x00 \x00 \x01 \x01 \x01 \x01 \x01 \x01 \x02 \x00 \x00 \x01 " , 15 ) ;
cmd . wlen = 15 ;
}
2014-07-13 10:52:19 -03:00
cmd . rlen = 1 ;
2014-12-06 14:40:06 -03:00
ret = si2157_cmd_execute ( client , & cmd ) ;
2019-11-14 21:03:56 +01:00
if ( ret & & ( dev - > chiptype ! = SI2157_CHIPTYPE_SI2141 | | ret ! = - EAGAIN ) )
2014-07-13 10:52:19 -03:00
goto err ;
2017-02-16 22:55:31 -02:00
/* Si2141 needs a second command before it answers the revision query */
if ( dev - > chiptype = = SI2157_CHIPTYPE_SI2141 ) {
memcpy ( cmd . args , " \xc0 \x08 \x01 \x02 \x00 \x00 \x01 " , 7 ) ;
cmd . wlen = 7 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
}
2019-10-10 06:51:01 -03:00
if ( dev - > dont_load_firmware ) {
dev_info ( & client - > dev , " device is buggy, skipping firmware download \n " ) ;
goto skip_fw_download ;
}
2014-07-13 10:52:19 -03:00
/* query chip revision */
memcpy ( cmd . args , " \x02 " , 1 ) ;
cmd . wlen = 1 ;
cmd . rlen = 13 ;
2014-12-06 14:40:06 -03:00
ret = si2157_cmd_execute ( client , & cmd ) ;
2014-07-13 10:52:19 -03:00
if ( ret )
goto err ;
2014-07-13 20:05:28 -03:00
chip_id = cmd . args [ 1 ] < < 24 | cmd . args [ 2 ] < < 16 | cmd . args [ 3 ] < < 8 |
cmd . args [ 4 ] < < 0 ;
2018-12-20 12:09:31 -05:00
# define SI2177_A30 ('A' << 24 | 77 << 16 | '3' << 8 | '0' << 0)
2014-07-13 20:05:28 -03:00
# define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0)
2014-11-14 18:19:37 -03:00
# define SI2148_A20 ('A' << 24 | 48 << 16 | '2' << 8 | '0' << 0)
2014-07-13 20:05:28 -03:00
# define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0)
2014-09-11 17:01:38 -03:00
# define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0)
2014-11-24 03:57:33 -03:00
# define SI2146_A10 ('A' << 24 | 46 << 16 | '1' << 8 | '0' << 0)
2017-02-16 22:55:31 -02:00
# define SI2141_A10 ('A' << 24 | 41 << 16 | '1' << 8 | '0' << 0)
2014-07-13 20:05:28 -03:00
switch ( chip_id ) {
case SI2158_A20 :
2014-11-14 18:19:37 -03:00
case SI2148_A20 :
2014-12-06 18:15:18 -03:00
fw_name = SI2158_A20_FIRMWARE ;
2014-07-13 20:05:28 -03:00
break ;
2017-02-16 22:55:31 -02:00
case SI2141_A10 :
fw_name = SI2141_A10_FIRMWARE ;
break ;
2018-12-20 12:09:31 -05:00
case SI2177_A30 :
fw_name = SI2157_A30_FIRMWARE ;
break ;
2014-07-13 20:05:28 -03:00
case SI2157_A30 :
2014-09-11 17:01:38 -03:00
case SI2147_A30 :
2014-11-24 03:57:33 -03:00
case SI2146_A10 :
2014-12-06 18:15:18 -03:00
fw_name = NULL ;
2014-12-06 18:12:39 -03:00
break ;
2014-07-13 20:05:28 -03:00
default :
2014-12-06 14:40:06 -03:00
dev_err ( & client - > dev , " unknown chip version Si21%d-%c%c%c \n " ,
2014-08-05 09:03:54 -03:00
cmd . args [ 2 ] , cmd . args [ 1 ] ,
2014-07-13 20:05:28 -03:00
cmd . args [ 3 ] , cmd . args [ 4 ] ) ;
ret = - EINVAL ;
goto err ;
}
2014-07-13 10:52:20 -03:00
2014-12-06 18:12:39 -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-12-06 18:15:18 -03:00
if ( fw_name = = NULL )
2014-12-06 18:12:39 -03:00
goto skip_fw_download ;
2014-07-13 10:52:20 -03:00
2014-07-13 20:05:28 -03:00
/* request the firmware, this will block and timeout */
2014-12-06 18:15:18 -03:00
ret = request_firmware ( & fw , fw_name , & client - > dev ) ;
2014-07-13 20:05:28 -03:00
if ( ret ) {
2014-12-06 14:40:06 -03:00
dev_err ( & client - > dev , " firmware file '%s' not found \n " ,
2014-12-06 18:15:18 -03:00
fw_name ) ;
2014-07-13 20:05:28 -03:00
goto err ;
}
2014-07-13 10:52:20 -03:00
2014-07-13 20:05:28 -03:00
/* firmware should be n chunks of 17 bytes */
if ( fw - > size % 17 ! = 0 ) {
2014-12-06 14:40:06 -03:00
dev_err ( & client - > dev , " firmware file '%s' is invalid \n " ,
2014-12-06 18:15:18 -03:00
fw_name ) ;
2014-07-13 20:05:28 -03:00
ret = - EINVAL ;
2014-12-06 14:51:17 -03:00
goto err_release_firmware ;
2014-07-13 20:05:28 -03:00
}
2014-07-13 10:52:20 -03:00
2014-12-06 14:40:06 -03:00
dev_info ( & client - > dev , " downloading firmware from file '%s' \n " ,
2014-12-06 18:15:18 -03:00
fw_name ) ;
2014-07-13 10:52:20 -03:00
2014-07-13 20:05:28 -03:00
for ( remaining = fw - > size ; remaining > 0 ; remaining - = 17 ) {
len = fw - > data [ fw - > size - remaining ] ;
2015-09-29 21:10:10 -03:00
if ( len > SI2157_ARGLEN ) {
dev_err ( & client - > dev , " Bad firmware length \n " ) ;
2015-10-05 19:33:29 -03:00
ret = - EINVAL ;
2015-09-29 21:10:10 -03:00
goto err_release_firmware ;
}
2014-07-13 20:05:28 -03:00
memcpy ( cmd . args , & fw - > data [ ( fw - > size - remaining ) + 1 ] , len ) ;
cmd . wlen = len ;
cmd . rlen = 1 ;
2014-12-06 14:40:06 -03:00
ret = si2157_cmd_execute ( client , & cmd ) ;
2014-07-13 20:05:28 -03:00
if ( ret ) {
2014-12-06 14:40:06 -03:00
dev_err ( & client - > dev , " firmware download failed %d \n " ,
2014-08-05 09:03:54 -03:00
ret ) ;
2014-12-06 14:51:17 -03:00
goto err_release_firmware ;
2014-07-13 10:52:20 -03:00
}
}
2014-07-13 20:05:28 -03:00
release_firmware ( fw ) ;
skip_fw_download :
2014-07-13 10:52:19 -03:00
/* reboot the tuner with new firmware? */
memcpy ( cmd . args , " \x01 \x01 " , 2 ) ;
cmd . wlen = 2 ;
cmd . rlen = 1 ;
2014-12-06 14:40:06 -03:00
ret = si2157_cmd_execute ( client , & cmd ) ;
2014-07-13 10:52:19 -03:00
if ( ret )
goto err ;
2014-12-06 17:25:24 -03:00
/* query firmware version */
memcpy ( cmd . args , " \x11 " , 1 ) ;
cmd . wlen = 1 ;
cmd . rlen = 10 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
dev_info ( & client - > dev , " firmware version: %c.%c.%d \n " ,
cmd . args [ 6 ] , cmd . args [ 7 ] , cmd . args [ 8 ] ) ;
2019-11-14 21:03:55 +01:00
/* enable tuner status flags */
memcpy ( cmd . args , " \x14 \x00 \x01 \x05 \x01 \x00 " , 6 ) ;
cmd . wlen = 6 ;
cmd . rlen = 1 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
memcpy ( cmd . args , " \x14 \x00 \x01 \x06 \x01 \x00 " , 6 ) ;
cmd . wlen = 6 ;
cmd . rlen = 1 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
memcpy ( cmd . args , " \x14 \x00 \x01 \x07 \x01 \x00 " , 6 ) ;
cmd . wlen = 6 ;
cmd . rlen = 1 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
2014-08-25 15:07:03 -03:00
warm :
2014-09-07 11:20:34 -03:00
/* init statistics in order signal app which are supported */
c - > strength . len = 1 ;
c - > strength . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
/* start statistics polling */
schedule_delayed_work ( & dev - > stat_work , msecs_to_jiffies ( 1000 ) ) ;
2014-12-05 17:46:30 -03:00
dev - > active = true ;
2014-04-10 21:58:10 -03:00
return 0 ;
2014-12-06 14:51:17 -03:00
err_release_firmware :
2014-11-30 15:05:48 -03:00
release_firmware ( fw ) ;
2014-11-30 16:48:24 -03:00
err :
2014-12-06 14:40:06 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-07-13 10:52:19 -03:00
return ret ;
2014-04-10 21:58:10 -03:00
}
static int si2157_sleep ( struct dvb_frontend * fe )
{
2014-12-06 14:40:06 -03:00
struct i2c_client * client = fe - > tuner_priv ;
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
2014-07-09 18:37:30 -03:00
int ret ;
struct si2157_cmd cmd ;
2014-04-10 21:58:10 -03:00
2014-12-06 14:40:06 -03:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-04-10 21:58:10 -03:00
2014-12-05 17:46:30 -03:00
dev - > active = false ;
2014-04-10 21:58:10 -03:00
2014-09-07 11:20:34 -03:00
/* stop statistics polling */
cancel_delayed_work_sync ( & dev - > stat_work ) ;
2014-08-25 15:07:02 -03:00
/* standby */
memcpy ( cmd . args , " \x16 \x00 " , 2 ) ;
cmd . wlen = 2 ;
cmd . rlen = 1 ;
2014-12-06 14:40:06 -03:00
ret = si2157_cmd_execute ( client , & cmd ) ;
2014-07-09 18:37:30 -03:00
if ( ret )
goto err ;
2014-04-10 21:58:10 -03:00
return 0 ;
2014-07-09 18:37:30 -03:00
err :
2014-12-06 14:40:06 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-07-09 18:37:30 -03:00
return ret ;
2014-04-10 21:58:10 -03:00
}
2019-11-14 21:04:00 +01:00
static int si2157_tune_wait ( struct i2c_client * client , u8 is_digital )
2019-11-14 21:03:59 +01:00
{
# define TUN_TIMEOUT 40
2019-11-14 21:04:00 +01:00
# define DIG_TIMEOUT 30
# define ANALOG_TIMEOUT 150
2019-11-14 21:03:59 +01:00
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
int ret ;
unsigned long timeout ;
unsigned long start_time ;
u8 wait_status ;
2019-11-14 21:04:00 +01:00
u8 tune_lock_mask ;
if ( is_digital )
tune_lock_mask = 0x04 ;
else
tune_lock_mask = 0x02 ;
2019-11-14 21:03:59 +01:00
mutex_lock ( & dev - > i2c_mutex ) ;
/* wait tuner command complete */
start_time = jiffies ;
timeout = start_time + msecs_to_jiffies ( TUN_TIMEOUT ) ;
while ( ! time_after ( jiffies , timeout ) ) {
ret = i2c_master_recv ( client , & wait_status ,
sizeof ( wait_status ) ) ;
if ( ret < 0 ) {
goto err_mutex_unlock ;
} else if ( ret ! = sizeof ( wait_status ) ) {
ret = - EREMOTEIO ;
goto err_mutex_unlock ;
}
/* tuner done? */
if ( ( wait_status & 0x81 ) = = 0x81 )
break ;
usleep_range ( 5000 , 10000 ) ;
}
dev_dbg ( & client - > dev , " tuning took %d ms, status=0x%x \n " ,
jiffies_to_msecs ( jiffies ) - jiffies_to_msecs ( start_time ) ,
wait_status ) ;
2019-11-14 21:04:00 +01:00
/* if we tuned ok, wait a bit for tuner lock */
if ( tuner_lock_debug & & ( wait_status & 0x81 ) = = 0x81 ) {
if ( is_digital )
timeout = jiffies + msecs_to_jiffies ( DIG_TIMEOUT ) ;
else
timeout = jiffies + msecs_to_jiffies ( ANALOG_TIMEOUT ) ;
while ( ! time_after ( jiffies , timeout ) ) {
ret = i2c_master_recv ( client , & wait_status ,
sizeof ( wait_status ) ) ;
if ( ret < 0 ) {
goto err_mutex_unlock ;
} else if ( ret ! = sizeof ( wait_status ) ) {
ret = - EREMOTEIO ;
goto err_mutex_unlock ;
}
/* tuner locked? */
if ( wait_status & tune_lock_mask )
break ;
usleep_range ( 5000 , 10000 ) ;
}
dev_dbg ( & client - > dev , " tuning+lock took %d ms, status=0x%x \n " ,
jiffies_to_msecs ( jiffies ) - jiffies_to_msecs ( start_time ) ,
wait_status ) ;
}
2019-11-14 21:03:59 +01:00
if ( ( wait_status & 0xc0 ) ! = 0x80 ) {
ret = - ETIMEDOUT ;
goto err_mutex_unlock ;
}
mutex_unlock ( & dev - > i2c_mutex ) ;
return 0 ;
err_mutex_unlock :
mutex_unlock ( & dev - > i2c_mutex ) ;
dev_err ( & client - > dev , " failed=%d \n " , ret ) ;
return ret ;
}
2014-04-10 21:58:10 -03:00
static int si2157_set_params ( struct dvb_frontend * fe )
{
2014-12-06 14:40:06 -03:00
struct i2c_client * client = fe - > tuner_priv ;
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
2014-04-10 21:58:10 -03:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
int ret ;
struct si2157_cmd cmd ;
2014-07-13 10:52:21 -03:00
u8 bandwidth , delivery_system ;
2015-02-28 12:25:23 -03:00
u32 if_frequency = 5000000 ;
2014-04-10 21:58:10 -03:00
2014-12-06 14:40:06 -03:00
dev_dbg ( & client - > dev ,
2014-08-05 09:03:54 -03:00
" delivery_system=%d frequency=%u bandwidth_hz=%u \n " ,
2014-12-06 14:40:06 -03:00
c - > delivery_system , c - > frequency , c - > bandwidth_hz ) ;
2014-04-10 21:58:10 -03:00
2014-12-05 17:46:30 -03:00
if ( ! dev - > active ) {
2014-04-10 21:58:10 -03:00
ret = - EAGAIN ;
goto err ;
}
2014-07-13 10:52:21 -03:00
if ( c - > bandwidth_hz < = 6000000 )
bandwidth = 0x06 ;
else if ( c - > bandwidth_hz < = 7000000 )
bandwidth = 0x07 ;
else if ( c - > bandwidth_hz < = 8000000 )
bandwidth = 0x08 ;
else
bandwidth = 0x0f ;
switch ( c - > delivery_system ) {
2014-08-17 02:24:49 -03:00
case SYS_ATSC :
delivery_system = 0x00 ;
2015-02-28 12:25:23 -03:00
if_frequency = 3250000 ;
2014-08-17 02:24:49 -03:00
break ;
2014-10-30 07:43:16 -03:00
case SYS_DVBC_ANNEX_B :
delivery_system = 0x10 ;
2015-02-28 12:25:23 -03:00
if_frequency = 4000000 ;
2014-10-30 07:43:16 -03:00
break ;
2014-07-13 10:52:21 -03:00
case SYS_DVBT :
case SYS_DVBT2 : /* it seems DVB-T and DVB-T2 both are 0x20 here */
delivery_system = 0x20 ;
break ;
case SYS_DVBC_ANNEX_A :
delivery_system = 0x30 ;
break ;
default :
ret = - EINVAL ;
goto err ;
}
memcpy ( cmd . args , " \x14 \x00 \x03 \x07 \x00 \x00 " , 6 ) ;
cmd . args [ 4 ] = delivery_system | bandwidth ;
2014-12-05 17:46:30 -03:00
if ( dev - > inversion )
2014-07-15 16:34:36 -03:00
cmd . args [ 5 ] = 0x01 ;
2014-07-13 10:52:21 -03:00
cmd . wlen = 6 ;
2014-09-11 17:01:38 -03:00
cmd . rlen = 4 ;
2014-12-06 14:40:06 -03:00
ret = si2157_cmd_execute ( client , & cmd ) ;
2014-09-11 17:01:38 -03:00
if ( ret )
goto err ;
2014-12-05 17:46:30 -03:00
if ( dev - > chiptype = = SI2157_CHIPTYPE_SI2146 )
2014-11-24 03:57:33 -03:00
memcpy ( cmd . args , " \x14 \x00 \x02 \x07 \x00 \x01 " , 6 ) ;
else
2015-05-05 13:54:17 -03:00
memcpy ( cmd . args , " \x14 \x00 \x02 \x07 \x00 \x00 " , 6 ) ;
cmd . args [ 4 ] = dev - > if_port ;
2014-09-11 17:01:38 -03:00
cmd . wlen = 6 ;
cmd . rlen = 4 ;
2014-12-06 14:40:06 -03:00
ret = si2157_cmd_execute ( client , & cmd ) ;
2014-07-13 10:52:21 -03:00
if ( ret )
goto err ;
2019-11-14 21:03:58 +01:00
/* set digital if frequency if needed */
2015-02-28 12:25:23 -03:00
if ( if_frequency ! = dev - > if_frequency ) {
memcpy ( cmd . args , " \x14 \x00 \x06 \x07 " , 4 ) ;
cmd . args [ 4 ] = ( if_frequency / 1000 ) & 0xff ;
cmd . args [ 5 ] = ( ( if_frequency / 1000 ) > > 8 ) & 0xff ;
cmd . wlen = 6 ;
cmd . rlen = 4 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
dev - > if_frequency = if_frequency ;
}
2019-11-14 21:03:58 +01:00
/* set digital frequency */
2014-07-13 10:52:19 -03:00
memcpy ( cmd . args , " \x41 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " , 8 ) ;
2014-04-10 21:58:10 -03:00
cmd . args [ 4 ] = ( c - > frequency > > 0 ) & 0xff ;
cmd . args [ 5 ] = ( c - > frequency > > 8 ) & 0xff ;
cmd . args [ 6 ] = ( c - > frequency > > 16 ) & 0xff ;
cmd . args [ 7 ] = ( c - > frequency > > 24 ) & 0xff ;
2014-07-10 06:02:53 -03:00
cmd . wlen = 8 ;
cmd . rlen = 1 ;
2014-12-06 14:40:06 -03:00
ret = si2157_cmd_execute ( client , & cmd ) ;
2014-04-10 21:58:10 -03:00
if ( ret )
goto err ;
2019-11-14 21:03:58 +01:00
dev - > bandwidth = bandwidth ;
dev - > frequency = c - > frequency ;
2019-11-14 21:04:00 +01:00
si2157_tune_wait ( client , 1 ) ; /* wait to complete, ignore any errors */
2019-11-14 21:03:59 +01:00
2014-04-10 21:58:10 -03:00
return 0 ;
err :
2019-11-14 21:03:58 +01:00
dev - > bandwidth = 0 ;
dev - > frequency = 0 ;
dev - > if_frequency = 0 ;
2014-12-06 14:40:06 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 21:58:10 -03:00
return ret ;
}
2019-11-14 21:03:58 +01:00
static int si2157_set_analog_params ( struct dvb_frontend * fe ,
struct analog_parameters * params )
{
struct i2c_client * client = fe - > tuner_priv ;
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
char * std ; /* for debugging */
int ret ;
struct si2157_cmd cmd ;
u32 bandwidth = 0 ;
u32 if_frequency = 0 ;
u32 freq = 0 ;
u64 tmp_lval = 0 ;
u8 system = 0 ;
u8 color = 0 ; /* 0=NTSC/PAL, 0x10=SECAM */
u8 invert_analog = 1 ; /* analog tuner spectrum; 0=normal, 1=inverted */
if ( dev - > chiptype ! = SI2157_CHIPTYPE_SI2157 ) {
dev_info ( & client - > dev , " Analog tuning not supported for chiptype=%u \n " ,
dev - > chiptype ) ;
ret = - EINVAL ;
goto err ;
}
if ( ! dev - > active )
si2157_init ( fe ) ;
if ( ! dev - > active ) {
ret = - EAGAIN ;
goto err ;
}
if ( params - > mode = = V4L2_TUNER_RADIO ) {
/*
* std = " fm " ;
* bandwidth = 1700000 ; //best can do for FM, AGC will be a mess though
* if_frequency = 1250000 ; //HVR-225x(saa7164), HVR-12xx(cx23885)
* if_frequency = 6600000 ; //HVR-9xx(cx231xx)
* if_frequency = 5500000 ; //HVR-19xx(pvrusb2)
*/
dev_err ( & client - > dev , " si2157 does not currently support FM radio \n " ) ;
ret = - EINVAL ;
goto err ;
}
tmp_lval = params - > frequency * 625LL ;
do_div ( tmp_lval , 10 ) ; /* convert to HZ */
freq = ( u32 ) tmp_lval ;
if ( freq < 1000000 ) /* is freq in KHz */
freq = freq * 1000 ;
dev - > frequency = freq ;
/* if_frequency values based on tda187271C2 */
if ( params - > std & ( V4L2_STD_B | V4L2_STD_GH ) ) {
if ( freq > = 470000000 ) {
std = " palGH " ;
bandwidth = 8000000 ;
if_frequency = 6000000 ;
system = 1 ;
if ( params - > std &
( V4L2_STD_SECAM_G | V4L2_STD_SECAM_H ) ) {
std = " secamGH " ;
color = 0x10 ;
}
} else {
std = " palB " ;
bandwidth = 7000000 ;
if_frequency = 6000000 ;
system = 0 ;
if ( params - > std & V4L2_STD_SECAM_B ) {
std = " secamB " ;
color = 0x10 ;
}
}
} else if ( params - > std & V4L2_STD_MN ) {
std = " MN " ;
bandwidth = 6000000 ;
if_frequency = 5400000 ;
system = 2 ;
} else if ( params - > std & V4L2_STD_PAL_I ) {
std = " palI " ;
bandwidth = 8000000 ;
if_frequency = 7250000 ; /* TODO: does not work yet */
system = 4 ;
} else if ( params - > std & V4L2_STD_DK ) {
std = " palDK " ;
bandwidth = 8000000 ;
if_frequency = 6900000 ; /* TODO: does not work yet */
system = 5 ;
if ( params - > std & V4L2_STD_SECAM_DK ) {
std = " secamDK " ;
color = 0x10 ;
}
} else if ( params - > std & V4L2_STD_SECAM_L ) {
std = " secamL " ;
bandwidth = 8000000 ;
if_frequency = 6750000 ; /* TODO: untested */
system = 6 ;
color = 0x10 ;
} else if ( params - > std & V4L2_STD_SECAM_LC ) {
std = " secamL' " ;
bandwidth = 7000000 ;
if_frequency = 1250000 ; /* TODO: untested */
system = 7 ;
color = 0x10 ;
} else {
std = " unknown " ;
}
/* calc channel center freq */
freq = freq - 1250000 + ( bandwidth / 2 ) ;
dev_dbg ( & client - > dev ,
" mode=%d system=%u std='%s' params->frequency=%u center freq=%u if=%u bandwidth=%u \n " ,
params - > mode , system , std , params - > frequency ,
freq , if_frequency , bandwidth ) ;
/* set analog IF port */
memcpy ( cmd . args , " \x14 \x00 \x03 \x06 \x08 \x02 " , 6 ) ;
/* in using dev->if_port, we assume analog and digital IF's */
/* are always on different ports */
/* assumes if_port definition is 0 or 1 for digital out */
cmd . args [ 4 ] = ( dev - > if_port = = 1 ) ? 8 : 10 ;
/* Analog AGC assumed external */
cmd . args [ 5 ] = ( dev - > if_port = = 1 ) ? 2 : 1 ;
cmd . wlen = 6 ;
cmd . rlen = 4 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
/* set analog IF output config */
memcpy ( cmd . args , " \x14 \x00 \x0d \x06 \x94 \x64 " , 6 ) ;
cmd . wlen = 6 ;
cmd . rlen = 4 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
/* make this distinct from a digital IF */
dev - > if_frequency = if_frequency | 1 ;
/* calc and set tuner analog if center frequency */
if_frequency = if_frequency + 1250000 - ( bandwidth / 2 ) ;
dev_dbg ( & client - > dev , " IF Ctr freq=%d \n " , if_frequency ) ;
memcpy ( cmd . args , " \x14 \x00 \x0C \x06 " , 4 ) ;
cmd . args [ 4 ] = ( if_frequency / 1000 ) & 0xff ;
cmd . args [ 5 ] = ( ( if_frequency / 1000 ) > > 8 ) & 0xff ;
cmd . wlen = 6 ;
cmd . rlen = 4 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
/* set analog AGC config */
memcpy ( cmd . args , " \x14 \x00 \x07 \x06 \x32 \xc8 " , 6 ) ;
cmd . wlen = 6 ;
cmd . rlen = 4 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
/* set analog video mode */
memcpy ( cmd . args , " \x14 \x00 \x04 \x06 \x00 \x00 " , 6 ) ;
cmd . args [ 4 ] = system | color ;
/* can use dev->inversion if assumed applies to both digital/analog */
if ( invert_analog )
cmd . args [ 5 ] | = 0x02 ;
cmd . wlen = 6 ;
cmd . rlen = 1 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
/* set analog frequency */
memcpy ( cmd . args , " \x41 \x01 \x00 \x00 \x00 \x00 \x00 \x00 " , 8 ) ;
cmd . args [ 4 ] = ( freq > > 0 ) & 0xff ;
cmd . args [ 5 ] = ( freq > > 8 ) & 0xff ;
cmd . args [ 6 ] = ( freq > > 16 ) & 0xff ;
cmd . args [ 7 ] = ( freq > > 24 ) & 0xff ;
cmd . wlen = 8 ;
cmd . rlen = 1 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
dev - > bandwidth = bandwidth ;
2019-11-14 21:04:00 +01:00
si2157_tune_wait ( client , 0 ) ; /* wait to complete, ignore any errors */
2019-11-14 21:03:59 +01:00
2019-11-14 21:03:58 +01:00
return 0 ;
err :
dev - > bandwidth = 0 ;
dev - > frequency = 0 ;
dev - > if_frequency = 0 ;
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
return ret ;
}
static int si2157_get_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
struct i2c_client * client = fe - > tuner_priv ;
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
* frequency = dev - > frequency ;
dev_dbg ( & client - > dev , " freq=%u \n " , dev - > frequency ) ;
return 0 ;
}
static int si2157_get_bandwidth ( struct dvb_frontend * fe , u32 * bandwidth )
{
struct i2c_client * client = fe - > tuner_priv ;
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
* bandwidth = dev - > bandwidth ;
dev_dbg ( & client - > dev , " bandwidth=%u \n " , dev - > bandwidth ) ;
return 0 ;
}
2014-07-15 04:58:40 -03:00
static int si2157_get_if_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
2015-02-28 12:25:23 -03:00
struct i2c_client * client = fe - > tuner_priv ;
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
2019-11-14 21:03:58 +01:00
* frequency = dev - > if_frequency & ~ 1 ; /* strip analog IF indicator bit */
dev_dbg ( & client - > dev , " if_frequency=%u \n " , * frequency ) ;
2014-07-15 04:58:40 -03:00
return 0 ;
}
2014-07-18 02:41:12 -03:00
static const struct dvb_tuner_ops si2157_ops = {
2014-04-10 21:58:10 -03:00
. info = {
2018-07-05 18:59:35 -04:00
. name = " Silicon Labs Si2141/Si2146/2147/2148/2157/2158 " ,
. frequency_min_hz = 42 * MHz ,
. frequency_max_hz = 870 * MHz ,
2014-04-10 21:58:10 -03:00
} ,
. init = si2157_init ,
. sleep = si2157_sleep ,
. set_params = si2157_set_params ,
2019-11-14 21:03:58 +01:00
. set_analog_params = si2157_set_analog_params ,
. get_frequency = si2157_get_frequency ,
. get_bandwidth = si2157_get_bandwidth ,
2014-07-15 04:58:40 -03:00
. get_if_frequency = si2157_get_if_frequency ,
2014-04-10 21:58:10 -03:00
} ;
2014-09-07 11:20:34 -03:00
static void si2157_stat_work ( struct work_struct * work )
{
struct si2157_dev * dev = container_of ( work , struct si2157_dev , stat_work . work ) ;
struct dvb_frontend * fe = dev - > fe ;
struct i2c_client * client = fe - > tuner_priv ;
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
struct si2157_cmd cmd ;
int ret ;
dev_dbg ( & client - > dev , " \n " ) ;
memcpy ( cmd . args , " \x42 \x00 " , 2 ) ;
cmd . wlen = 2 ;
cmd . rlen = 12 ;
ret = si2157_cmd_execute ( client , & cmd ) ;
if ( ret )
goto err ;
c - > strength . stat [ 0 ] . scale = FE_SCALE_DECIBEL ;
c - > strength . stat [ 0 ] . svalue = ( s8 ) cmd . args [ 3 ] * 1000 ;
schedule_delayed_work ( & dev - > stat_work , msecs_to_jiffies ( 2000 ) ) ;
return ;
err :
c - > strength . stat [ 0 ] . scale = FE_SCALE_NOT_AVAILABLE ;
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
}
2014-04-10 21:58:10 -03:00
static int si2157_probe ( struct i2c_client * client ,
2016-02-11 18:06:36 -02:00
const struct i2c_device_id * id )
2014-04-10 21:58:10 -03:00
{
struct si2157_config * cfg = client - > dev . platform_data ;
struct dvb_frontend * fe = cfg - > fe ;
2014-12-05 17:46:30 -03:00
struct si2157_dev * dev ;
2014-04-10 21:58:10 -03:00
struct si2157_cmd cmd ;
int ret ;
2014-12-05 17:46:30 -03:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
2014-04-10 21:58:10 -03:00
ret = - ENOMEM ;
2014-08-05 09:03:54 -03:00
dev_err ( & client - > dev , " kzalloc() failed \n " ) ;
2014-04-10 21:58:10 -03:00
goto err ;
}
2014-12-06 14:40:06 -03:00
i2c_set_clientdata ( client , dev ) ;
2014-12-05 17:46:30 -03:00
dev - > fe = cfg - > fe ;
dev - > inversion = cfg - > inversion ;
2019-10-10 06:51:01 -03:00
dev - > dont_load_firmware = cfg - > dont_load_firmware ;
2015-05-05 13:54:17 -03:00
dev - > if_port = cfg - > if_port ;
2014-12-05 17:46:30 -03:00
dev - > chiptype = ( u8 ) id - > driver_data ;
2015-02-28 12:25:23 -03:00
dev - > if_frequency = 5000000 ; /* default value of property 0x0706 */
2014-12-05 17:46:30 -03:00
mutex_init ( & dev - > i2c_mutex ) ;
2014-09-07 11:20:34 -03:00
INIT_DELAYED_WORK ( & dev - > stat_work , si2157_stat_work ) ;
2014-04-10 21:58:10 -03:00
/* check if the tuner is there */
2014-07-10 06:02:53 -03:00
cmd . wlen = 0 ;
cmd . rlen = 1 ;
2014-12-06 14:40:06 -03:00
ret = si2157_cmd_execute ( client , & cmd ) ;
2019-11-14 21:03:56 +01:00
if ( ret & & ret ! = - EAGAIN )
2014-12-06 15:13:31 -03:00
goto err_kfree ;
2014-04-10 21:58:10 -03:00
2014-12-05 17:46:30 -03:00
memcpy ( & fe - > ops . tuner_ops , & si2157_ops , sizeof ( struct dvb_tuner_ops ) ) ;
2014-12-06 14:40:06 -03:00
fe - > tuner_priv = client ;
2014-04-10 21:58:10 -03:00
2016-02-11 18:06:36 -02:00
# ifdef CONFIG_MEDIA_CONTROLLER
if ( cfg - > mdev ) {
dev - > mdev = cfg - > mdev ;
dev - > ent . name = KBUILD_MODNAME ;
dev - > ent . function = MEDIA_ENT_F_TUNER ;
2018-08-01 06:15:22 -04:00
dev - > pad [ SI2157_PAD_RF_INPUT ] . flags = MEDIA_PAD_FL_SINK ;
dev - > pad [ SI2157_PAD_RF_INPUT ] . sig_type = PAD_SIGNAL_ANALOG ;
dev - > pad [ SI2157_PAD_VID_OUT ] . flags = MEDIA_PAD_FL_SOURCE ;
dev - > pad [ SI2157_PAD_VID_OUT ] . sig_type = PAD_SIGNAL_ANALOG ;
dev - > pad [ SI2157_PAD_AUD_OUT ] . flags = MEDIA_PAD_FL_SOURCE ;
dev - > pad [ SI2157_PAD_AUD_OUT ] . sig_type = PAD_SIGNAL_AUDIO ;
ret = media_entity_pads_init ( & dev - > ent , SI2157_NUM_PADS ,
2016-02-11 18:06:36 -02:00
& dev - > pad [ 0 ] ) ;
if ( ret )
goto err_kfree ;
ret = media_device_register_entity ( cfg - > mdev , & dev - > ent ) ;
if ( ret ) {
media_entity_cleanup ( & dev - > ent ) ;
goto err_kfree ;
}
}
# endif
2014-12-06 14:40:06 -03:00
dev_info ( & client - > dev , " Silicon Labs %s successfully attached \n " ,
2017-02-16 22:55:31 -02:00
dev - > chiptype = = SI2157_CHIPTYPE_SI2141 ? " Si2141 " :
2017-04-06 09:17:42 -03:00
dev - > chiptype = = SI2157_CHIPTYPE_SI2146 ?
2014-11-14 18:19:37 -03:00
" Si2146 " : " Si2147/2148/2157/2158 " ) ;
2014-11-24 03:57:33 -03:00
2014-04-10 21:58:10 -03:00
return 0 ;
2014-12-06 15:13:31 -03:00
err_kfree :
kfree ( dev ) ;
2014-04-10 21:58:10 -03:00
err :
2014-08-05 09:03:54 -03:00
dev_dbg ( & client - > dev , " failed=%d \n " , ret ) ;
2014-04-10 21:58:10 -03:00
return ret ;
}
static int si2157_remove ( struct i2c_client * client )
{
2014-12-05 17:46:30 -03:00
struct si2157_dev * dev = i2c_get_clientdata ( client ) ;
struct dvb_frontend * fe = dev - > fe ;
2014-04-10 21:58:10 -03:00
2014-08-05 09:03:54 -03:00
dev_dbg ( & client - > dev , " \n " ) ;
2014-04-10 21:58:10 -03:00
2016-01-09 18:18:43 -02:00
/* stop statistics polling */
cancel_delayed_work_sync ( & dev - > stat_work ) ;
2016-02-11 18:06:36 -02:00
# ifdef CONFIG_MEDIA_CONTROLLER_DVB
if ( dev - > mdev )
media_device_unregister_entity ( & dev - > ent ) ;
# endif
2014-04-10 21:58:10 -03:00
memset ( & fe - > ops . tuner_ops , 0 , sizeof ( struct dvb_tuner_ops ) ) ;
fe - > tuner_priv = NULL ;
2014-12-05 17:46:30 -03:00
kfree ( dev ) ;
2014-04-10 21:58:10 -03:00
return 0 ;
}
2014-12-06 15:07:45 -03:00
static const struct i2c_device_id si2157_id_table [ ] = {
{ " si2157 " , SI2157_CHIPTYPE_SI2157 } ,
{ " si2146 " , SI2157_CHIPTYPE_SI2146 } ,
2017-02-16 22:55:31 -02:00
{ " si2141 " , SI2157_CHIPTYPE_SI2141 } ,
2018-12-20 12:09:31 -05:00
{ " si2177 " , SI2157_CHIPTYPE_SI2177 } ,
2014-04-10 21:58:10 -03:00
{ }
} ;
2014-12-06 15:07:45 -03:00
MODULE_DEVICE_TABLE ( i2c , si2157_id_table ) ;
2014-04-10 21:58:10 -03:00
static struct i2c_driver si2157_driver = {
. driver = {
2018-01-04 06:47:28 -05:00
. name = " si2157 " ,
2016-06-29 20:38:19 -03:00
. suppress_bind_attrs = true ,
2014-04-10 21:58:10 -03:00
} ,
. probe = si2157_probe ,
. remove = si2157_remove ,
2014-12-06 15:07:45 -03:00
. id_table = si2157_id_table ,
2014-04-10 21:58:10 -03:00
} ;
module_i2c_driver ( si2157_driver ) ;
2017-02-16 22:55:31 -02:00
MODULE_DESCRIPTION ( " Silicon Labs Si2141/Si2146/2147/2148/2157/2158 silicon tuner driver " ) ;
2014-04-10 21:58:10 -03:00
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2014-07-13 16:13:49 -03:00
MODULE_FIRMWARE ( SI2158_A20_FIRMWARE ) ;
2017-02-16 22:55:31 -02:00
MODULE_FIRMWARE ( SI2141_A10_FIRMWARE ) ;
2018-12-20 12:09:31 -05:00
MODULE_FIRMWARE ( SI2157_A30_FIRMWARE ) ;