2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2009-11-14 04:33:45 +03:00
/*
* E3C EC100 demodulator driver
*
* Copyright ( C ) 2009 Antti Palosaari < crope @ iki . fi >
*/
2017-12-28 21:03:51 +03:00
# include <media/dvb_frontend.h>
2009-11-14 04:33:45 +03:00
# include "ec100.h"
struct ec100_state {
struct i2c_adapter * i2c ;
struct dvb_frontend frontend ;
struct ec100_config config ;
u16 ber ;
} ;
/* write single register */
static int ec100_write_reg ( struct ec100_state * state , u8 reg , u8 val )
{
2012-09-13 03:23:45 +04:00
int ret ;
2009-11-14 04:33:45 +03:00
u8 buf [ 2 ] = { reg , val } ;
2012-09-13 03:23:45 +04:00
struct i2c_msg msg [ 1 ] = {
{
. addr = state - > config . demod_address ,
. flags = 0 ,
. len = sizeof ( buf ) ,
. buf = buf ,
}
} ;
2009-11-14 04:33:45 +03:00
2012-09-13 03:23:45 +04:00
ret = i2c_transfer ( state - > i2c , msg , 1 ) ;
if ( ret = = 1 ) {
ret = 0 ;
} else {
dev_warn ( & state - > i2c - > dev , " %s: i2c wr failed=%d reg=%02x \n " ,
KBUILD_MODNAME , ret , reg ) ;
ret = - EREMOTEIO ;
2009-11-14 04:33:45 +03:00
}
2012-09-13 03:23:45 +04:00
return ret ;
2009-11-14 04:33:45 +03:00
}
/* read single register */
static int ec100_read_reg ( struct ec100_state * state , u8 reg , u8 * val )
{
2012-09-13 03:23:45 +04:00
int ret ;
2009-11-14 04:33:45 +03:00
struct i2c_msg msg [ 2 ] = {
{
. addr = state - > config . demod_address ,
. flags = 0 ,
. len = 1 ,
. buf = & reg
} , {
. addr = state - > config . demod_address ,
. flags = I2C_M_RD ,
. len = 1 ,
. buf = val
}
} ;
2012-09-13 03:23:45 +04:00
ret = i2c_transfer ( state - > i2c , msg , 2 ) ;
if ( ret = = 2 ) {
ret = 0 ;
} else {
dev_warn ( & state - > i2c - > dev , " %s: i2c rd failed=%d reg=%02x \n " ,
KBUILD_MODNAME , ret , reg ) ;
ret = - EREMOTEIO ;
2009-11-14 04:33:45 +03:00
}
2012-09-13 03:23:45 +04:00
return ret ;
2009-11-14 04:33:45 +03:00
}
2011-12-26 17:15:30 +04:00
static int ec100_set_frontend ( struct dvb_frontend * fe )
2009-11-14 04:33:45 +03:00
{
2011-12-26 17:15:30 +04:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2009-11-14 04:33:45 +03:00
struct ec100_state * state = fe - > demodulator_priv ;
int ret ;
u8 tmp , tmp2 ;
2012-09-13 03:23:44 +04:00
dev_dbg ( & state - > i2c - > dev , " %s: frequency=%d bandwidth_hz=%d \n " ,
__func__ , c - > frequency , c - > bandwidth_hz ) ;
2009-11-14 04:33:45 +03:00
/* program tuner */
if ( fe - > ops . tuner_ops . set_params )
2011-12-24 19:24:33 +04:00
fe - > ops . tuner_ops . set_params ( fe ) ;
2009-11-14 04:33:45 +03:00
ret = ec100_write_reg ( state , 0x04 , 0x06 ) ;
if ( ret )
goto error ;
ret = ec100_write_reg ( state , 0x67 , 0x58 ) ;
if ( ret )
goto error ;
ret = ec100_write_reg ( state , 0x05 , 0x18 ) ;
if ( ret )
goto error ;
/* reg/bw | 6 | 7 | 8
- - - - - - - + - - - - - - + - - - - - - + - - - - - -
A 0x1b | 0xa1 | 0xe7 | 0x2c
A 0x1c | 0x55 | 0x63 | 0x72
- - - - - - - + - - - - - - + - - - - - - + - - - - - -
B 0x1b | 0xb7 | 0x00 | 0x49
B 0x1c | 0x55 | 0x64 | 0x72 */
2011-12-26 17:15:30 +04:00
switch ( c - > bandwidth_hz ) {
case 6000000 :
2009-11-14 04:33:45 +03:00
tmp = 0xb7 ;
tmp2 = 0x55 ;
break ;
2011-12-26 17:15:30 +04:00
case 7000000 :
2009-11-14 04:33:45 +03:00
tmp = 0x00 ;
tmp2 = 0x64 ;
break ;
2011-12-26 17:15:30 +04:00
case 8000000 :
2009-11-14 04:33:45 +03:00
default :
tmp = 0x49 ;
tmp2 = 0x72 ;
}
ret = ec100_write_reg ( state , 0x1b , tmp ) ;
if ( ret )
goto error ;
ret = ec100_write_reg ( state , 0x1c , tmp2 ) ;
if ( ret )
goto error ;
ret = ec100_write_reg ( state , 0x0c , 0xbb ) ; /* if freq */
if ( ret )
goto error ;
ret = ec100_write_reg ( state , 0x0d , 0x31 ) ; /* if freq */
if ( ret )
goto error ;
ret = ec100_write_reg ( state , 0x08 , 0x24 ) ;
if ( ret )
goto error ;
ret = ec100_write_reg ( state , 0x00 , 0x00 ) ; /* go */
if ( ret )
goto error ;
ret = ec100_write_reg ( state , 0x00 , 0x20 ) ; /* go */
if ( ret )
goto error ;
return ret ;
error :
2012-09-13 03:23:44 +04:00
dev_dbg ( & state - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2009-11-14 04:33:45 +03:00
return ret ;
}
static int ec100_get_tune_settings ( struct dvb_frontend * fe ,
struct dvb_frontend_tune_settings * fesettings )
{
fesettings - > min_delay_ms = 300 ;
fesettings - > step_size = 0 ;
fesettings - > max_drift = 0 ;
return 0 ;
}
2015-06-07 20:53:52 +03:00
static int ec100_read_status ( struct dvb_frontend * fe , enum fe_status * status )
2009-11-14 04:33:45 +03:00
{
struct ec100_state * state = fe - > demodulator_priv ;
int ret ;
u8 tmp ;
* status = 0 ;
ret = ec100_read_reg ( state , 0x42 , & tmp ) ;
if ( ret )
goto error ;
if ( tmp & 0x80 ) {
/* bit7 set - have lock */
* status | = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
FE_HAS_SYNC | FE_HAS_LOCK ;
} else {
ret = ec100_read_reg ( state , 0x01 , & tmp ) ;
if ( ret )
goto error ;
if ( tmp & 0x10 ) {
/* bit4 set - have signal */
* status | = FE_HAS_SIGNAL ;
if ( ! ( tmp & 0x01 ) ) {
/* bit0 clear - have ~valid signal */
* status | = FE_HAS_CARRIER | FE_HAS_VITERBI ;
}
}
}
return ret ;
error :
2012-09-13 03:23:44 +04:00
dev_dbg ( & state - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2009-11-14 04:33:45 +03:00
return ret ;
}
static int ec100_read_ber ( struct dvb_frontend * fe , u32 * ber )
{
struct ec100_state * state = fe - > demodulator_priv ;
int ret ;
u8 tmp , tmp2 ;
u16 ber2 ;
* ber = 0 ;
ret = ec100_read_reg ( state , 0x65 , & tmp ) ;
if ( ret )
goto error ;
ret = ec100_read_reg ( state , 0x66 , & tmp2 ) ;
if ( ret )
goto error ;
ber2 = ( tmp2 < < 8 ) | tmp ;
/* if counter overflow or clear */
if ( ber2 < state - > ber )
* ber = ber2 ;
else
* ber = ber2 - state - > ber ;
state - > ber = ber2 ;
return ret ;
error :
2012-09-13 03:23:44 +04:00
dev_dbg ( & state - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2009-11-14 04:33:45 +03:00
return ret ;
}
static int ec100_read_signal_strength ( struct dvb_frontend * fe , u16 * strength )
{
struct ec100_state * state = fe - > demodulator_priv ;
int ret ;
u8 tmp ;
ret = ec100_read_reg ( state , 0x24 , & tmp ) ;
if ( ret ) {
* strength = 0 ;
goto error ;
}
* strength = ( ( tmp < < 8 ) | tmp ) ;
return ret ;
error :
2012-09-13 03:23:44 +04:00
dev_dbg ( & state - > i2c - > dev , " %s: failed=%d \n " , __func__ , ret ) ;
2009-11-14 04:33:45 +03:00
return ret ;
}
static int ec100_read_snr ( struct dvb_frontend * fe , u16 * snr )
{
* snr = 0 ;
return 0 ;
}
static int ec100_read_ucblocks ( struct dvb_frontend * fe , u32 * ucblocks )
{
* ucblocks = 0 ;
return 0 ;
}
static void ec100_release ( struct dvb_frontend * fe )
{
struct ec100_state * state = fe - > demodulator_priv ;
kfree ( state ) ;
}
2016-08-10 00:32:21 +03:00
static const struct dvb_frontend_ops ec100_ops ;
2009-11-14 04:33:45 +03:00
struct dvb_frontend * ec100_attach ( const struct ec100_config * config ,
struct i2c_adapter * i2c )
{
int ret ;
struct ec100_state * state = NULL ;
u8 tmp ;
/* allocate memory for the internal state */
state = kzalloc ( sizeof ( struct ec100_state ) , GFP_KERNEL ) ;
if ( state = = NULL )
goto error ;
/* setup the state */
state - > i2c = i2c ;
memcpy ( & state - > config , config , sizeof ( struct ec100_config ) ) ;
/* check if the demod is there */
ret = ec100_read_reg ( state , 0x33 , & tmp ) ;
if ( ret | | tmp ! = 0x0b )
goto error ;
/* create dvb_frontend */
memcpy ( & state - > frontend . ops , & ec100_ops ,
sizeof ( struct dvb_frontend_ops ) ) ;
state - > frontend . demodulator_priv = state ;
return & state - > frontend ;
error :
kfree ( state ) ;
return NULL ;
}
EXPORT_SYMBOL ( ec100_attach ) ;
2016-08-10 00:32:21 +03:00
static const struct dvb_frontend_ops ec100_ops = {
2011-12-26 17:15:30 +04:00
. delsys = { SYS_DVBT } ,
2009-11-14 04:33:45 +03:00
. info = {
. name = " E3C EC100 DVB-T " ,
. 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_64 | FE_CAN_QAM_AUTO |
FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_HIERARCHY_AUTO |
FE_CAN_MUTE_TS
} ,
. release = ec100_release ,
2011-12-26 17:15:30 +04:00
. set_frontend = ec100_set_frontend ,
2009-11-14 04:33:45 +03:00
. get_tune_settings = ec100_get_tune_settings ,
. read_status = ec100_read_status ,
. read_ber = ec100_read_ber ,
. read_signal_strength = ec100_read_signal_strength ,
. read_snr = ec100_read_snr ,
. read_ucblocks = ec100_read_ucblocks ,
} ;
MODULE_AUTHOR ( " Antti Palosaari <crope@iki.fi> " ) ;
MODULE_DESCRIPTION ( " E3C EC100 DVB-T demodulator driver " ) ;
MODULE_LICENSE ( " GPL " ) ;