2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2008-09-19 00:48:31 -03:00
/*
* TerraTec Cinergy T2 / qanu USB2 DVB - T adapter .
*
* Copyright ( C ) 2007 Tomi Orava ( tomimo @ ncircle . nullnet . fi )
*
* Based on the dvb - usb - framework code and the
* original Terratec Cinergy T2 driver by :
*
* Copyright ( C ) 2004 Daniel Mack < daniel @ qanu . de > and
* Holger Waechtler < holger @ qanu . de >
*
* Protocol Spec published on http : //qanu.de/specs/terratec_cinergyT2.pdf
*/
# include "cinergyT2.h"
2017-11-29 08:33:45 -05:00
/*
2008-09-19 00:48:31 -03:00
* convert linux - dvb frontend parameter set into TPS .
* See ETSI ETS - 300744 , section 4.6 .2 , table 9 for details .
*
* This function is probably reusable and may better get placed in a support
* library .
*
2019-02-18 14:29:03 -05:00
* We replace erroneous fields by default TPS fields ( the ones with value 0 ) .
2008-09-19 00:48:31 -03:00
*/
2011-12-26 15:41:01 -03:00
static uint16_t compute_tps ( struct dtv_frontend_properties * op )
2008-09-19 00:48:31 -03:00
{
uint16_t tps = 0 ;
switch ( op - > code_rate_HP ) {
case FEC_2_3 :
tps | = ( 1 < < 7 ) ;
break ;
case FEC_3_4 :
tps | = ( 2 < < 7 ) ;
break ;
case FEC_5_6 :
tps | = ( 3 < < 7 ) ;
break ;
case FEC_7_8 :
tps | = ( 4 < < 7 ) ;
break ;
case FEC_1_2 :
case FEC_AUTO :
default :
/* tps |= (0 << 7) */ ;
}
switch ( op - > code_rate_LP ) {
case FEC_2_3 :
tps | = ( 1 < < 4 ) ;
break ;
case FEC_3_4 :
tps | = ( 2 < < 4 ) ;
break ;
case FEC_5_6 :
tps | = ( 3 < < 4 ) ;
break ;
case FEC_7_8 :
tps | = ( 4 < < 4 ) ;
break ;
case FEC_1_2 :
case FEC_AUTO :
default :
/* tps |= (0 << 4) */ ;
}
2011-12-26 15:41:01 -03:00
switch ( op - > modulation ) {
2008-09-19 00:48:31 -03:00
case QAM_16 :
tps | = ( 1 < < 13 ) ;
break ;
case QAM_64 :
tps | = ( 2 < < 13 ) ;
break ;
case QPSK :
default :
/* tps |= (0 << 13) */ ;
}
switch ( op - > transmission_mode ) {
case TRANSMISSION_MODE_8K :
tps | = ( 1 < < 0 ) ;
break ;
case TRANSMISSION_MODE_2K :
default :
/* tps |= (0 << 0) */ ;
}
switch ( op - > guard_interval ) {
case GUARD_INTERVAL_1_16 :
tps | = ( 1 < < 2 ) ;
break ;
case GUARD_INTERVAL_1_8 :
tps | = ( 2 < < 2 ) ;
break ;
case GUARD_INTERVAL_1_4 :
tps | = ( 3 < < 2 ) ;
break ;
case GUARD_INTERVAL_1_32 :
default :
/* tps |= (0 << 2) */ ;
}
2011-12-26 15:41:01 -03:00
switch ( op - > hierarchy ) {
2008-09-19 00:48:31 -03:00
case HIERARCHY_1 :
tps | = ( 1 < < 10 ) ;
break ;
case HIERARCHY_2 :
tps | = ( 2 < < 10 ) ;
break ;
case HIERARCHY_4 :
tps | = ( 3 < < 10 ) ;
break ;
case HIERARCHY_NONE :
default :
/* tps |= (0 << 10) */ ;
}
return tps ;
}
struct cinergyt2_fe_state {
struct dvb_frontend fe ;
struct dvb_usb_device * d ;
2016-10-07 06:16:59 -03:00
unsigned char data [ 64 ] ;
struct mutex data_mutex ;
2016-10-07 06:07:36 -03:00
struct dvbt_get_status_msg status ;
2008-09-19 00:48:31 -03:00
} ;
static int cinergyt2_fe_read_status ( struct dvb_frontend * fe ,
2015-06-07 14:53:52 -03:00
enum fe_status * status )
2008-09-19 00:48:31 -03:00
{
struct cinergyt2_fe_state * state = fe - > demodulator_priv ;
int ret ;
2016-10-07 06:16:59 -03:00
mutex_lock ( & state - > data_mutex ) ;
state - > data [ 0 ] = CINERGYT2_EP1_GET_TUNER_STATUS ;
ret = dvb_usb_generic_rw ( state - > d , state - > data , 1 ,
state - > data , sizeof ( state - > status ) , 0 ) ;
if ( ! ret )
memcpy ( & state - > status , state - > data , sizeof ( state - > status ) ) ;
mutex_unlock ( & state - > data_mutex ) ;
2008-09-19 00:48:31 -03:00
if ( ret < 0 )
return ret ;
* status = 0 ;
2016-10-07 06:16:59 -03:00
if ( 0xffff - le16_to_cpu ( state - > status . gain ) > 30 )
2008-09-19 00:48:31 -03:00
* status | = FE_HAS_SIGNAL ;
2016-10-07 06:16:59 -03:00
if ( state - > status . lock_bits & ( 1 < < 6 ) )
2008-09-19 00:48:31 -03:00
* status | = FE_HAS_LOCK ;
2016-10-07 06:16:59 -03:00
if ( state - > status . lock_bits & ( 1 < < 5 ) )
2008-09-19 00:48:31 -03:00
* status | = FE_HAS_SYNC ;
2016-10-07 06:16:59 -03:00
if ( state - > status . lock_bits & ( 1 < < 4 ) )
2008-09-19 00:48:31 -03:00
* status | = FE_HAS_CARRIER ;
2016-10-07 06:16:59 -03:00
if ( state - > status . lock_bits & ( 1 < < 1 ) )
2008-09-19 00:48:31 -03:00
* status | = FE_HAS_VITERBI ;
if ( ( * status & ( FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC ) ) ! =
( FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC ) )
* status & = ~ FE_HAS_LOCK ;
return 0 ;
}
static int cinergyt2_fe_read_ber ( struct dvb_frontend * fe , u32 * ber )
{
struct cinergyt2_fe_state * state = fe - > demodulator_priv ;
2016-10-07 06:07:36 -03:00
* ber = le32_to_cpu ( state - > status . viterbi_error_rate ) ;
2008-09-19 00:48:31 -03:00
return 0 ;
}
static int cinergyt2_fe_read_unc_blocks ( struct dvb_frontend * fe , u32 * unc )
{
struct cinergyt2_fe_state * state = fe - > demodulator_priv ;
2016-10-07 06:07:36 -03:00
* unc = le32_to_cpu ( state - > status . uncorrected_block_count ) ;
2008-09-19 00:48:31 -03:00
return 0 ;
}
static int cinergyt2_fe_read_signal_strength ( struct dvb_frontend * fe ,
u16 * strength )
{
struct cinergyt2_fe_state * state = fe - > demodulator_priv ;
2016-10-07 06:07:36 -03:00
* strength = ( 0xffff - le16_to_cpu ( state - > status . gain ) ) ;
2008-09-19 00:48:31 -03:00
return 0 ;
}
static int cinergyt2_fe_read_snr ( struct dvb_frontend * fe , u16 * snr )
{
struct cinergyt2_fe_state * state = fe - > demodulator_priv ;
2016-10-07 06:07:36 -03:00
* snr = ( state - > status . snr < < 8 ) | state - > status . snr ;
2008-09-19 00:48:31 -03:00
return 0 ;
}
static int cinergyt2_fe_init ( struct dvb_frontend * fe )
{
return 0 ;
}
static int cinergyt2_fe_sleep ( struct dvb_frontend * fe )
{
deb_info ( " cinergyt2_fe_sleep() Called \n " ) ;
return 0 ;
}
static int cinergyt2_fe_get_tune_settings ( struct dvb_frontend * fe ,
struct dvb_frontend_tune_settings * tune )
{
tune - > min_delay_ms = 800 ;
return 0 ;
}
2011-12-26 15:41:01 -03:00
static int cinergyt2_fe_set_frontend ( struct dvb_frontend * fe )
2008-09-19 00:48:31 -03:00
{
2011-12-26 15:41:01 -03:00
struct dtv_frontend_properties * fep = & fe - > dtv_property_cache ;
2008-09-19 00:48:31 -03:00
struct cinergyt2_fe_state * state = fe - > demodulator_priv ;
2016-10-07 06:16:59 -03:00
struct dvbt_set_parameters_msg * param ;
2008-09-19 00:48:31 -03:00
int err ;
2016-10-07 06:16:59 -03:00
mutex_lock ( & state - > data_mutex ) ;
param = ( void * ) state - > data ;
param - > cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS ;
param - > tps = cpu_to_le16 ( compute_tps ( fep ) ) ;
param - > freq = cpu_to_le32 ( fep - > frequency / 1000 ) ;
param - > flags = 0 ;
2008-09-19 00:48:31 -03:00
2011-12-26 15:41:01 -03:00
switch ( fep - > bandwidth_hz ) {
2012-01-23 13:15:22 -02:00
default :
2011-12-26 15:41:01 -03:00
case 8000000 :
2016-10-07 06:16:59 -03:00
param - > bandwidth = 8 ;
2011-12-26 15:41:01 -03:00
break ;
case 7000000 :
2016-10-07 06:16:59 -03:00
param - > bandwidth = 7 ;
2011-12-26 15:41:01 -03:00
break ;
case 6000000 :
2016-10-07 06:16:59 -03:00
param - > bandwidth = 6 ;
2011-12-26 15:41:01 -03:00
break ;
}
2016-10-07 06:16:59 -03:00
err = dvb_usb_generic_rw ( state - > d , state - > data , sizeof ( * param ) ,
state - > data , 2 , 0 ) ;
2008-09-19 00:48:31 -03:00
if ( err < 0 )
err ( " cinergyt2_fe_set_frontend() Failed! err=%d \n " , err ) ;
2016-10-07 06:16:59 -03:00
mutex_unlock ( & state - > data_mutex ) ;
2008-09-19 00:48:31 -03:00
return ( err < 0 ) ? err : 0 ;
}
static void cinergyt2_fe_release ( struct dvb_frontend * fe )
{
struct cinergyt2_fe_state * state = fe - > demodulator_priv ;
2013-02-26 15:35:01 -03:00
kfree ( state ) ;
2008-09-19 00:48:31 -03:00
}
2016-08-09 18:32:21 -03:00
static const struct dvb_frontend_ops cinergyt2_fe_ops ;
2008-09-19 00:48:31 -03:00
struct dvb_frontend * cinergyt2_fe_attach ( struct dvb_usb_device * d )
{
struct cinergyt2_fe_state * s = kzalloc ( sizeof (
struct cinergyt2_fe_state ) , GFP_KERNEL ) ;
if ( s = = NULL )
return NULL ;
s - > d = d ;
memcpy ( & s - > fe . ops , & cinergyt2_fe_ops , sizeof ( struct dvb_frontend_ops ) ) ;
s - > fe . demodulator_priv = s ;
2016-10-07 06:16:59 -03:00
mutex_init ( & s - > data_mutex ) ;
2008-09-19 00:48:31 -03:00
return & s - > fe ;
}
2016-08-09 18:32:21 -03:00
static const struct dvb_frontend_ops cinergyt2_fe_ops = {
2011-12-26 15:41:01 -03:00
. delsys = { SYS_DVBT } ,
2008-09-19 00:48:31 -03:00
. info = {
. name = DRIVER_NAME ,
2018-07-05 18:59:36 -04:00
. frequency_min_hz = 174 * MHz ,
. frequency_max_hz = 862 * MHz ,
. frequency_stepsize_hz = 166667 ,
2008-09-19 00:48:31 -03:00
. caps = FE_CAN_INVERSION_AUTO | 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_RECOVER
| FE_CAN_MUTE_TS
} ,
. release = cinergyt2_fe_release ,
. init = cinergyt2_fe_init ,
. sleep = cinergyt2_fe_sleep ,
2011-12-26 15:41:01 -03:00
. set_frontend = cinergyt2_fe_set_frontend ,
2008-09-19 00:48:31 -03:00
. get_tune_settings = cinergyt2_fe_get_tune_settings ,
. read_status = cinergyt2_fe_read_status ,
. read_ber = cinergyt2_fe_read_ber ,
. read_signal_strength = cinergyt2_fe_read_signal_strength ,
. read_snr = cinergyt2_fe_read_snr ,
. read_ucblocks = cinergyt2_fe_read_unc_blocks ,
} ;