2019-05-20 10:19:02 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
Conexant cx22700 DVB OFDM demodulator driver
Copyright ( C ) 2001 - 2002 Convergence Integrated Media GmbH
Holger Waechtler < holger @ convergence . de >
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/string.h>
# include <linux/slab.h>
2017-12-28 21:03:51 +03:00
# include <media/dvb_frontend.h>
2005-04-17 02:20:36 +04:00
# include "cx22700.h"
struct cx22700_state {
struct i2c_adapter * i2c ;
const struct cx22700_config * config ;
struct dvb_frontend frontend ;
} ;
static int debug ;
# define dprintk(args...) \
do { \
if ( debug ) printk ( KERN_DEBUG " cx22700: " args ) ; \
} while ( 0 )
static u8 init_tab [ ] = {
0x04 , 0x10 ,
0x05 , 0x09 ,
0x06 , 0x00 ,
0x08 , 0x04 ,
0x09 , 0x00 ,
0x0a , 0x01 ,
0x15 , 0x40 ,
0x16 , 0x10 ,
0x17 , 0x87 ,
0x18 , 0x17 ,
0x1a , 0x10 ,
0x25 , 0x04 ,
0x2e , 0x00 ,
0x39 , 0x00 ,
0x3a , 0x04 ,
0x45 , 0x08 ,
0x46 , 0x02 ,
0x47 , 0x05 ,
} ;
static int cx22700_writereg ( struct cx22700_state * state , u8 reg , u8 data )
{
int ret ;
u8 buf [ ] = { reg , data } ;
struct i2c_msg msg = { . addr = state - > config - > demod_address , . flags = 0 , . buf = buf , . len = 2 } ;
2008-04-09 06:20:00 +04:00
dprintk ( " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
ret = i2c_transfer ( state - > i2c , & msg , 1 ) ;
if ( ret ! = 1 )
printk ( " %s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i) \n " ,
2008-04-09 06:20:00 +04:00
__func__ , reg , data , ret ) ;
2005-04-17 02:20:36 +04:00
return ( ret ! = 1 ) ? - 1 : 0 ;
}
static int cx22700_readreg ( struct cx22700_state * state , u8 reg )
{
int ret ;
u8 b0 [ ] = { reg } ;
u8 b1 [ ] = { 0 } ;
struct i2c_msg msg [ ] = { { . addr = state - > config - > demod_address , . flags = 0 , . buf = b0 , . len = 1 } ,
{ . addr = state - > config - > demod_address , . flags = I2C_M_RD , . buf = b1 , . len = 1 } } ;
2008-04-09 06:20:00 +04:00
dprintk ( " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
ret = i2c_transfer ( state - > i2c , msg , 2 ) ;
if ( ret ! = 2 ) return - EIO ;
return b1 [ 0 ] ;
}
static int cx22700_set_inversion ( struct cx22700_state * state , int inversion )
{
u8 val ;
2008-04-09 06:20:00 +04:00
dprintk ( " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
switch ( inversion ) {
case INVERSION_AUTO :
return - EOPNOTSUPP ;
case INVERSION_ON :
val = cx22700_readreg ( state , 0x09 ) ;
return cx22700_writereg ( state , 0x09 , val | 0x01 ) ;
case INVERSION_OFF :
val = cx22700_readreg ( state , 0x09 ) ;
return cx22700_writereg ( state , 0x09 , val & 0xfe ) ;
default :
return - EINVAL ;
}
}
2011-12-22 17:14:08 +04:00
static int cx22700_set_tps ( struct cx22700_state * state ,
struct dtv_frontend_properties * p )
2005-04-17 02:20:36 +04:00
{
static const u8 qam_tab [ 4 ] = { 0 , 1 , 0 , 2 } ;
static const u8 fec_tab [ 6 ] = { 0 , 1 , 2 , 0 , 3 , 4 } ;
u8 val ;
2008-04-09 06:20:00 +04:00
dprintk ( " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( p - > code_rate_HP < FEC_1_2 | | p - > code_rate_HP > FEC_7_8 )
return - EINVAL ;
if ( p - > code_rate_LP < FEC_1_2 | | p - > code_rate_LP > FEC_7_8 )
2006-06-30 18:51:11 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( p - > code_rate_HP = = FEC_4_5 | | p - > code_rate_LP = = FEC_4_5 )
return - EINVAL ;
2012-10-27 23:14:01 +04:00
if ( ( int ) p - > guard_interval < GUARD_INTERVAL_1_32 | |
2005-04-17 02:20:36 +04:00
p - > guard_interval > GUARD_INTERVAL_1_4 )
return - EINVAL ;
if ( p - > transmission_mode ! = TRANSMISSION_MODE_2K & &
p - > transmission_mode ! = TRANSMISSION_MODE_8K )
return - EINVAL ;
2011-12-22 17:14:08 +04:00
if ( p - > modulation ! = QPSK & &
p - > modulation ! = QAM_16 & &
p - > modulation ! = QAM_64 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2012-10-27 23:14:01 +04:00
if ( ( int ) p - > hierarchy < HIERARCHY_NONE | |
2011-12-22 17:14:08 +04:00
p - > hierarchy > HIERARCHY_4 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2011-12-22 17:14:08 +04:00
if ( p - > bandwidth_hz > 8000000 | | p - > bandwidth_hz < 6000000 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2011-12-22 17:14:08 +04:00
if ( p - > bandwidth_hz = = 7000000 )
2005-04-17 02:20:36 +04:00
cx22700_writereg ( state , 0x09 , cx22700_readreg ( state , 0x09 | 0x10 ) ) ;
else
cx22700_writereg ( state , 0x09 , cx22700_readreg ( state , 0x09 & ~ 0x10 ) ) ;
2011-12-22 17:14:08 +04:00
val = qam_tab [ p - > modulation - QPSK ] ;
val | = p - > hierarchy - HIERARCHY_NONE ;
2005-04-17 02:20:36 +04:00
cx22700_writereg ( state , 0x04 , val ) ;
2014-11-05 14:42:22 +03:00
if ( p - > code_rate_HP - FEC_1_2 > = sizeof ( fec_tab ) | |
p - > code_rate_LP - FEC_1_2 > = sizeof ( fec_tab ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
val = fec_tab [ p - > code_rate_HP - FEC_1_2 ] < < 3 ;
val | = fec_tab [ p - > code_rate_LP - FEC_1_2 ] ;
cx22700_writereg ( state , 0x05 , val ) ;
val = ( p - > guard_interval - GUARD_INTERVAL_1_32 ) < < 2 ;
val | = p - > transmission_mode - TRANSMISSION_MODE_2K ;
cx22700_writereg ( state , 0x06 , val ) ;
cx22700_writereg ( state , 0x08 , 0x04 | 0x02 ) ; /* use user tps parameters */
2011-03-31 05:57:33 +04:00
cx22700_writereg ( state , 0x08 , 0x04 ) ; /* restart acquisition */
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-12-22 17:14:08 +04:00
static int cx22700_get_tps ( struct cx22700_state * state ,
struct dtv_frontend_properties * p )
2005-04-17 02:20:36 +04:00
{
2015-06-07 20:53:52 +03:00
static const enum fe_modulation qam_tab [ 3 ] = { QPSK , QAM_16 , QAM_64 } ;
static const enum fe_code_rate fec_tab [ 5 ] = {
FEC_1_2 , FEC_2_3 , FEC_3_4 , FEC_5_6 , FEC_7_8
} ;
2005-04-17 02:20:36 +04:00
u8 val ;
2008-04-09 06:20:00 +04:00
dprintk ( " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( ! ( cx22700_readreg ( state , 0x07 ) & 0x20 ) ) /* tps valid? */
return - EAGAIN ;
val = cx22700_readreg ( state , 0x01 ) ;
if ( ( val & 0x7 ) > 4 )
2011-12-22 17:14:08 +04:00
p - > hierarchy = HIERARCHY_AUTO ;
2005-04-17 02:20:36 +04:00
else
2011-12-22 17:14:08 +04:00
p - > hierarchy = HIERARCHY_NONE + ( val & 0x7 ) ;
2005-04-17 02:20:36 +04:00
if ( ( ( val > > 3 ) & 0x3 ) > 2 )
2011-12-22 17:14:08 +04:00
p - > modulation = QAM_AUTO ;
2005-04-17 02:20:36 +04:00
else
2011-12-22 17:14:08 +04:00
p - > modulation = qam_tab [ ( val > > 3 ) & 0x3 ] ;
2005-04-17 02:20:36 +04:00
val = cx22700_readreg ( state , 0x02 ) ;
if ( ( ( val > > 3 ) & 0x07 ) > 4 )
p - > code_rate_HP = FEC_AUTO ;
else
p - > code_rate_HP = fec_tab [ ( val > > 3 ) & 0x07 ] ;
if ( ( val & 0x07 ) > 4 )
p - > code_rate_LP = FEC_AUTO ;
else
p - > code_rate_LP = fec_tab [ val & 0x07 ] ;
val = cx22700_readreg ( state , 0x03 ) ;
p - > guard_interval = GUARD_INTERVAL_1_32 + ( ( val > > 6 ) & 0x3 ) ;
p - > transmission_mode = TRANSMISSION_MODE_2K + ( ( val > > 5 ) & 0x1 ) ;
return 0 ;
}
static int cx22700_init ( struct dvb_frontend * fe )
2005-05-17 08:54:31 +04:00
{ struct cx22700_state * state = fe - > demodulator_priv ;
2005-04-17 02:20:36 +04:00
int i ;
dprintk ( " cx22700_init: init chip \n " ) ;
cx22700_writereg ( state , 0x00 , 0x02 ) ; /* soft reset */
cx22700_writereg ( state , 0x00 , 0x00 ) ;
msleep ( 10 ) ;
for ( i = 0 ; i < sizeof ( init_tab ) ; i + = 2 )
cx22700_writereg ( state , init_tab [ i ] , init_tab [ i + 1 ] ) ;
cx22700_writereg ( state , 0x00 , 0x01 ) ;
return 0 ;
}
2015-06-07 20:53:52 +03:00
static int cx22700_read_status ( struct dvb_frontend * fe , enum fe_status * status )
2005-04-17 02:20:36 +04:00
{
2005-05-17 08:54:31 +04:00
struct cx22700_state * state = fe - > demodulator_priv ;
2005-04-17 02:20:36 +04:00
u16 rs_ber = ( cx22700_readreg ( state , 0x0d ) < < 9 )
| ( cx22700_readreg ( state , 0x0e ) < < 1 ) ;
u8 sync = cx22700_readreg ( state , 0x07 ) ;
* status = 0 ;
if ( rs_ber < 0xff00 )
* status | = FE_HAS_SIGNAL ;
if ( sync & 0x20 )
* status | = FE_HAS_CARRIER ;
if ( sync & 0x10 )
* status | = FE_HAS_VITERBI ;
if ( sync & 0x10 )
* status | = FE_HAS_SYNC ;
if ( * status = = 0x0f )
* status | = FE_HAS_LOCK ;
return 0 ;
}
static int cx22700_read_ber ( struct dvb_frontend * fe , u32 * ber )
{
2005-05-17 08:54:31 +04:00
struct cx22700_state * state = fe - > demodulator_priv ;
2005-04-17 02:20:36 +04:00
* ber = cx22700_readreg ( state , 0x0c ) & 0x7f ;
cx22700_writereg ( state , 0x0c , 0x00 ) ;
return 0 ;
}
static int cx22700_read_signal_strength ( struct dvb_frontend * fe , u16 * signal_strength )
{
2005-05-17 08:54:31 +04:00
struct cx22700_state * state = fe - > demodulator_priv ;
2005-04-17 02:20:36 +04:00
u16 rs_ber = ( cx22700_readreg ( state , 0x0d ) < < 9 )
| ( cx22700_readreg ( state , 0x0e ) < < 1 ) ;
* signal_strength = ~ rs_ber ;
return 0 ;
}
static int cx22700_read_snr ( struct dvb_frontend * fe , u16 * snr )
{
2005-05-17 08:54:31 +04:00
struct cx22700_state * state = fe - > demodulator_priv ;
2005-04-17 02:20:36 +04:00
u16 rs_ber = ( cx22700_readreg ( state , 0x0d ) < < 9 )
| ( cx22700_readreg ( state , 0x0e ) < < 1 ) ;
* snr = ~ rs_ber ;
return 0 ;
}
static int cx22700_read_ucblocks ( struct dvb_frontend * fe , u32 * ucblocks )
{
2005-05-17 08:54:31 +04:00
struct cx22700_state * state = fe - > demodulator_priv ;
2005-04-17 02:20:36 +04:00
* ucblocks = cx22700_readreg ( state , 0x0f ) ;
cx22700_writereg ( state , 0x0f , 0x00 ) ;
return 0 ;
}
2011-12-22 17:14:08 +04:00
static int cx22700_set_frontend ( struct dvb_frontend * fe )
2005-04-17 02:20:36 +04:00
{
2011-12-22 17:14:08 +04:00
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
2005-05-17 08:54:31 +04:00
struct cx22700_state * state = fe - > demodulator_priv ;
2005-04-17 02:20:36 +04:00
cx22700_writereg ( state , 0x00 , 0x02 ) ; /* XXX CHECKME: soft reset*/
cx22700_writereg ( state , 0x00 , 0x00 ) ;
2006-05-14 12:01:31 +04:00
if ( fe - > ops . tuner_ops . set_params ) {
2011-12-24 19:24:33 +04:00
fe - > ops . tuner_ops . set_params ( fe ) ;
2006-05-14 12:01:31 +04:00
if ( fe - > ops . i2c_gate_ctrl ) fe - > ops . i2c_gate_ctrl ( fe , 0 ) ;
2006-04-19 00:47:09 +04:00
}
2011-12-22 17:14:08 +04:00
cx22700_set_inversion ( state , c - > inversion ) ;
cx22700_set_tps ( state , c ) ;
2005-04-17 02:20:36 +04:00
cx22700_writereg ( state , 0x37 , 0x01 ) ; /* PAL loop filter off */
cx22700_writereg ( state , 0x00 , 0x01 ) ; /* restart acquire */
return 0 ;
}
2016-02-04 17:58:30 +03:00
static int cx22700_get_frontend ( struct dvb_frontend * fe ,
struct dtv_frontend_properties * c )
2005-04-17 02:20:36 +04:00
{
2005-05-17 08:54:31 +04:00
struct cx22700_state * state = fe - > demodulator_priv ;
2005-04-17 02:20:36 +04:00
u8 reg09 = cx22700_readreg ( state , 0x09 ) ;
2011-12-22 17:14:08 +04:00
c - > inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF ;
return cx22700_get_tps ( state , c ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-19 00:47:09 +04:00
static int cx22700_i2c_gate_ctrl ( struct dvb_frontend * fe , int enable )
{
struct cx22700_state * state = fe - > demodulator_priv ;
if ( enable ) {
return cx22700_writereg ( state , 0x0a , 0x00 ) ;
} else {
return cx22700_writereg ( state , 0x0a , 0x01 ) ;
}
}
2005-04-17 02:20:36 +04:00
static int cx22700_get_tune_settings ( struct dvb_frontend * fe , struct dvb_frontend_tune_settings * fesettings )
{
2005-12-12 11:37:24 +03:00
fesettings - > min_delay_ms = 150 ;
fesettings - > step_size = 166667 ;
fesettings - > max_drift = 166667 * 2 ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static void cx22700_release ( struct dvb_frontend * fe )
{
2005-05-17 08:54:31 +04:00
struct cx22700_state * state = fe - > demodulator_priv ;
2005-04-17 02:20:36 +04:00
kfree ( state ) ;
}
2016-08-10 00:32:21 +03:00
static const struct dvb_frontend_ops cx22700_ops ;
2005-04-17 02:20:36 +04:00
struct dvb_frontend * cx22700_attach ( const struct cx22700_config * config ,
struct i2c_adapter * i2c )
{
struct cx22700_state * state = NULL ;
/* allocate memory for the internal state */
2009-08-11 05:51:01 +04:00
state = kzalloc ( sizeof ( struct cx22700_state ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( state = = NULL ) goto error ;
/* setup the state */
state - > config = config ;
state - > i2c = i2c ;
/* check if the demod is there */
if ( cx22700_readreg ( state , 0x07 ) < 0 ) goto error ;
/* create dvb_frontend */
2006-05-14 12:01:31 +04:00
memcpy ( & state - > frontend . ops , & cx22700_ops , sizeof ( struct dvb_frontend_ops ) ) ;
2005-04-17 02:20:36 +04:00
state - > frontend . demodulator_priv = state ;
return & state - > frontend ;
error :
kfree ( state ) ;
return NULL ;
}
2016-08-10 00:32:21 +03:00
static const struct dvb_frontend_ops cx22700_ops = {
2011-12-22 17:14:08 +04:00
. delsys = { SYS_DVBT } ,
2005-04-17 02:20:36 +04:00
. info = {
. name = " Conexant CX22700 DVB-T " ,
2018-07-06 01:59:36 +03:00
. frequency_min_hz = 470 * MHz ,
. frequency_max_hz = 860 * MHz ,
. frequency_stepsize_hz = 166667 ,
2005-04-17 02:20:36 +04: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_64 |
2005-12-12 11:37:24 +03:00
FE_CAN_RECOVER
2005-04-17 02:20:36 +04:00
} ,
. release = cx22700_release ,
. init = cx22700_init ,
2006-04-19 00:47:09 +04:00
. i2c_gate_ctrl = cx22700_i2c_gate_ctrl ,
2005-04-17 02:20:36 +04:00
2011-12-22 17:14:08 +04:00
. set_frontend = cx22700_set_frontend ,
. get_frontend = cx22700_get_frontend ,
2005-04-17 02:20:36 +04:00
. get_tune_settings = cx22700_get_tune_settings ,
. read_status = cx22700_read_status ,
. read_ber = cx22700_read_ber ,
. read_signal_strength = cx22700_read_signal_strength ,
. read_snr = cx22700_read_snr ,
. read_ucblocks = cx22700_read_ucblocks ,
} ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " Turn on/off frontend debugging (default:off). " ) ;
MODULE_DESCRIPTION ( " Conexant CX22700 DVB-T Demodulator driver " ) ;
MODULE_AUTHOR ( " Holger Waechtler " ) ;
MODULE_LICENSE ( " GPL " ) ;
EXPORT_SYMBOL ( cx22700_attach ) ;