2019-05-23 12:14:55 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-10-31 19:24:39 +04:00
/*
* Abilis Systems Single DVB - T Receiver
* Copyright ( C ) 2008 Pierrick Hascoet < pierrick . hascoet @ abilis . com >
2011-10-31 19:24:44 +04:00
* Copyright ( C ) 2010 Devin Heitmueller < dheitmueller @ kernellabs . com >
2011-10-31 19:24:39 +04:00
*/
2014-08-13 01:50:22 +04:00
2017-12-28 21:03:51 +03:00
# include <media/dvb_frontend.h>
2014-08-13 01:50:22 +04:00
# include "as102_fe.h"
2011-10-31 19:24:39 +04:00
2014-08-13 01:50:19 +04:00
struct as102_state {
struct dvb_frontend frontend ;
struct as10x_demod_stats demod_stats ;
2011-10-31 19:24:39 +04:00
2014-08-13 01:50:22 +04:00
const struct as102_fe_ops * ops ;
void * priv ;
2014-08-13 01:50:19 +04:00
uint8_t elna_cfg ;
2011-10-31 19:24:39 +04:00
2014-08-13 01:50:19 +04:00
/* signal strength */
uint16_t signal_strength ;
/* bit error rate */
uint32_t ber ;
2011-10-31 19:24:39 +04:00
} ;
2015-06-07 20:53:52 +03:00
static uint8_t as102_fe_get_code_rate ( enum fe_code_rate arg )
2011-10-31 19:24:44 +04:00
{
2011-10-31 19:24:39 +04:00
uint8_t c ;
2011-10-31 19:24:44 +04:00
switch ( arg ) {
case FEC_1_2 :
c = CODE_RATE_1_2 ;
break ;
case FEC_2_3 :
c = CODE_RATE_2_3 ;
break ;
case FEC_3_4 :
c = CODE_RATE_3_4 ;
break ;
case FEC_5_6 :
c = CODE_RATE_5_6 ;
break ;
case FEC_7_8 :
c = CODE_RATE_7_8 ;
break ;
default :
c = CODE_RATE_UNKNOWN ;
break ;
2011-10-31 19:24:39 +04:00
}
return c ;
}
2014-08-13 01:50:20 +04:00
static int as102_fe_set_frontend ( struct dvb_frontend * fe )
2011-10-31 19:24:44 +04:00
{
2014-08-13 01:50:20 +04:00
struct as102_state * state = fe - > demodulator_priv ;
struct dtv_frontend_properties * c = & fe - > dtv_property_cache ;
struct as10x_tune_args tune_args = { 0 } ;
2011-10-31 19:24:39 +04:00
/* set frequency */
2014-08-13 01:50:20 +04:00
tune_args . freq = c - > frequency / 1000 ;
2011-10-31 19:24:39 +04:00
/* fix interleaving_mode */
2014-08-13 01:50:20 +04:00
tune_args . interleaving_mode = INTLV_NATIVE ;
2011-10-31 19:24:39 +04:00
2014-08-13 01:50:20 +04:00
switch ( c - > bandwidth_hz ) {
2011-12-26 22:20:03 +04:00
case 8000000 :
2014-08-13 01:50:20 +04:00
tune_args . bandwidth = BW_8_MHZ ;
2011-10-31 19:24:44 +04:00
break ;
2011-12-26 22:20:03 +04:00
case 7000000 :
2014-08-13 01:50:20 +04:00
tune_args . bandwidth = BW_7_MHZ ;
2011-10-31 19:24:44 +04:00
break ;
2011-12-26 22:20:03 +04:00
case 6000000 :
2014-08-13 01:50:20 +04:00
tune_args . bandwidth = BW_6_MHZ ;
2011-10-31 19:24:44 +04:00
break ;
default :
2014-08-13 01:50:20 +04:00
tune_args . bandwidth = BW_8_MHZ ;
2011-10-31 19:24:39 +04:00
}
2014-08-13 01:50:20 +04:00
switch ( c - > guard_interval ) {
2011-10-31 19:24:44 +04:00
case GUARD_INTERVAL_1_32 :
2014-08-13 01:50:20 +04:00
tune_args . guard_interval = GUARD_INT_1_32 ;
2011-10-31 19:24:44 +04:00
break ;
case GUARD_INTERVAL_1_16 :
2014-08-13 01:50:20 +04:00
tune_args . guard_interval = GUARD_INT_1_16 ;
2011-10-31 19:24:44 +04:00
break ;
case GUARD_INTERVAL_1_8 :
2014-08-13 01:50:20 +04:00
tune_args . guard_interval = GUARD_INT_1_8 ;
2011-10-31 19:24:44 +04:00
break ;
case GUARD_INTERVAL_1_4 :
2014-08-13 01:50:20 +04:00
tune_args . guard_interval = GUARD_INT_1_4 ;
2011-10-31 19:24:44 +04:00
break ;
case GUARD_INTERVAL_AUTO :
default :
2014-08-13 01:50:20 +04:00
tune_args . guard_interval = GUARD_UNKNOWN ;
2011-10-31 19:24:44 +04:00
break ;
2011-10-31 19:24:39 +04:00
}
2014-08-13 01:50:20 +04:00
switch ( c - > modulation ) {
2011-10-31 19:24:44 +04:00
case QPSK :
2014-08-13 01:50:20 +04:00
tune_args . modulation = CONST_QPSK ;
2011-10-31 19:24:44 +04:00
break ;
case QAM_16 :
2014-08-13 01:50:20 +04:00
tune_args . modulation = CONST_QAM16 ;
2011-10-31 19:24:44 +04:00
break ;
case QAM_64 :
2014-08-13 01:50:20 +04:00
tune_args . modulation = CONST_QAM64 ;
2011-10-31 19:24:44 +04:00
break ;
default :
2014-08-13 01:50:20 +04:00
tune_args . modulation = CONST_UNKNOWN ;
2011-10-31 19:24:44 +04:00
break ;
2011-10-31 19:24:39 +04:00
}
2014-08-13 01:50:20 +04:00
switch ( c - > transmission_mode ) {
2011-10-31 19:24:44 +04:00
case TRANSMISSION_MODE_2K :
2014-08-13 01:50:20 +04:00
tune_args . transmission_mode = TRANS_MODE_2K ;
2011-10-31 19:24:44 +04:00
break ;
case TRANSMISSION_MODE_8K :
2014-08-13 01:50:20 +04:00
tune_args . transmission_mode = TRANS_MODE_8K ;
2011-10-31 19:24:44 +04:00
break ;
default :
2014-08-13 01:50:20 +04:00
tune_args . transmission_mode = TRANS_MODE_UNKNOWN ;
2011-10-31 19:24:39 +04:00
}
2014-08-13 01:50:20 +04:00
switch ( c - > hierarchy ) {
2011-10-31 19:24:44 +04:00
case HIERARCHY_NONE :
2014-08-13 01:50:20 +04:00
tune_args . hierarchy = HIER_NONE ;
2011-10-31 19:24:44 +04:00
break ;
case HIERARCHY_1 :
2014-08-13 01:50:20 +04:00
tune_args . hierarchy = HIER_ALPHA_1 ;
2011-10-31 19:24:44 +04:00
break ;
case HIERARCHY_2 :
2014-08-13 01:50:20 +04:00
tune_args . hierarchy = HIER_ALPHA_2 ;
2011-10-31 19:24:44 +04:00
break ;
case HIERARCHY_4 :
2014-08-13 01:50:20 +04:00
tune_args . hierarchy = HIER_ALPHA_4 ;
2011-10-31 19:24:44 +04:00
break ;
case HIERARCHY_AUTO :
2014-08-13 01:50:20 +04:00
tune_args . hierarchy = HIER_UNKNOWN ;
2011-10-31 19:24:44 +04:00
break ;
2011-10-31 19:24:39 +04:00
}
2014-08-04 15:13:16 +04:00
pr_debug ( " as102: tuner parameters: freq: %d bw: 0x%02x gi: 0x%02x \n " ,
2014-08-13 01:50:20 +04:00
c - > frequency ,
tune_args . bandwidth ,
tune_args . guard_interval ) ;
2011-10-31 19:24:39 +04:00
/*
* Detect a hierarchy selection
* if HP / LP are both set to FEC_NONE , HP will be selected .
*/
2014-08-13 01:50:20 +04:00
if ( ( tune_args . hierarchy ! = HIER_NONE ) & &
( ( c - > code_rate_LP = = FEC_NONE ) | |
( c - > code_rate_HP = = FEC_NONE ) ) ) {
if ( c - > code_rate_LP = = FEC_NONE ) {
tune_args . hier_select = HIER_HIGH_PRIORITY ;
tune_args . code_rate =
as102_fe_get_code_rate ( c - > code_rate_HP ) ;
2011-10-31 19:24:39 +04:00
}
2014-08-13 01:50:20 +04:00
if ( c - > code_rate_HP = = FEC_NONE ) {
tune_args . hier_select = HIER_LOW_PRIORITY ;
tune_args . code_rate =
as102_fe_get_code_rate ( c - > code_rate_LP ) ;
2011-10-31 19:24:39 +04:00
}
2014-08-04 15:13:16 +04:00
pr_debug ( " as102: \t hierarchy: 0x%02x selected: %s code_rate_%s: 0x%02x \n " ,
2014-08-13 01:50:20 +04:00
tune_args . hierarchy ,
tune_args . hier_select = = HIER_HIGH_PRIORITY ?
2011-10-31 19:24:44 +04:00
" HP " : " LP " ,
2014-08-13 01:50:20 +04:00
tune_args . hier_select = = HIER_HIGH_PRIORITY ?
2011-10-31 19:24:44 +04:00
" HP " : " LP " ,
2014-08-13 01:50:20 +04:00
tune_args . code_rate ) ;
2011-10-31 19:24:39 +04:00
} else {
2014-08-13 01:50:20 +04:00
tune_args . code_rate =
as102_fe_get_code_rate ( c - > code_rate_HP ) ;
2011-10-31 19:24:39 +04:00
}
2014-08-13 01:50:19 +04:00
2014-08-13 01:50:20 +04:00
/* Set frontend arguments */
2014-08-13 01:50:22 +04:00
return state - > ops - > set_tune ( state - > priv , & tune_args ) ;
2014-08-13 01:50:19 +04:00
}
2016-02-04 17:58:30 +03:00
static int as102_fe_get_frontend ( struct dvb_frontend * fe ,
struct dtv_frontend_properties * c )
2014-08-13 01:50:19 +04:00
{
struct as102_state * state = fe - > demodulator_priv ;
int ret = 0 ;
struct as10x_tps tps = { 0 } ;
/* send abilis command: GET_TPS */
2014-08-13 01:50:22 +04:00
ret = state - > ops - > get_tps ( state - > priv , & tps ) ;
2014-08-13 01:50:21 +04:00
if ( ret < 0 )
return ret ;
/* extract constellation */
switch ( tps . modulation ) {
case CONST_QPSK :
c - > modulation = QPSK ;
break ;
case CONST_QAM16 :
c - > modulation = QAM_16 ;
break ;
case CONST_QAM64 :
c - > modulation = QAM_64 ;
break ;
}
/* extract hierarchy */
switch ( tps . hierarchy ) {
case HIER_NONE :
c - > hierarchy = HIERARCHY_NONE ;
break ;
case HIER_ALPHA_1 :
c - > hierarchy = HIERARCHY_1 ;
break ;
case HIER_ALPHA_2 :
c - > hierarchy = HIERARCHY_2 ;
break ;
case HIER_ALPHA_4 :
c - > hierarchy = HIERARCHY_4 ;
break ;
}
/* extract code rate HP */
switch ( tps . code_rate_HP ) {
case CODE_RATE_1_2 :
c - > code_rate_HP = FEC_1_2 ;
break ;
case CODE_RATE_2_3 :
c - > code_rate_HP = FEC_2_3 ;
break ;
case CODE_RATE_3_4 :
c - > code_rate_HP = FEC_3_4 ;
break ;
case CODE_RATE_5_6 :
c - > code_rate_HP = FEC_5_6 ;
break ;
case CODE_RATE_7_8 :
c - > code_rate_HP = FEC_7_8 ;
break ;
}
/* extract code rate LP */
switch ( tps . code_rate_LP ) {
case CODE_RATE_1_2 :
c - > code_rate_LP = FEC_1_2 ;
break ;
case CODE_RATE_2_3 :
c - > code_rate_LP = FEC_2_3 ;
break ;
case CODE_RATE_3_4 :
c - > code_rate_LP = FEC_3_4 ;
break ;
case CODE_RATE_5_6 :
c - > code_rate_LP = FEC_5_6 ;
break ;
case CODE_RATE_7_8 :
c - > code_rate_LP = FEC_7_8 ;
break ;
}
/* extract guard interval */
switch ( tps . guard_interval ) {
case GUARD_INT_1_32 :
c - > guard_interval = GUARD_INTERVAL_1_32 ;
break ;
case GUARD_INT_1_16 :
c - > guard_interval = GUARD_INTERVAL_1_16 ;
break ;
case GUARD_INT_1_8 :
c - > guard_interval = GUARD_INTERVAL_1_8 ;
break ;
case GUARD_INT_1_4 :
c - > guard_interval = GUARD_INTERVAL_1_4 ;
break ;
}
/* extract transmission mode */
switch ( tps . transmission_mode ) {
case TRANS_MODE_2K :
c - > transmission_mode = TRANSMISSION_MODE_2K ;
break ;
case TRANS_MODE_8K :
c - > transmission_mode = TRANSMISSION_MODE_8K ;
break ;
}
return 0 ;
2014-08-13 01:50:19 +04:00
}
static int as102_fe_get_tune_settings ( struct dvb_frontend * fe ,
2019-11-12 22:52:48 +03:00
struct dvb_frontend_tune_settings * settings )
{
2014-08-13 01:50:19 +04:00
settings - > min_delay_ms = 1000 ;
return 0 ;
}
2015-06-07 20:53:52 +03:00
static int as102_fe_read_status ( struct dvb_frontend * fe , enum fe_status * status )
2014-08-13 01:50:19 +04:00
{
int ret = 0 ;
struct as102_state * state = fe - > demodulator_priv ;
struct as10x_tune_status tstate = { 0 } ;
/* send abilis command: GET_TUNE_STATUS */
2014-08-13 01:50:22 +04:00
ret = state - > ops - > get_status ( state - > priv , & tstate ) ;
if ( ret < 0 )
return ret ;
2014-08-13 01:50:19 +04:00
state - > signal_strength = tstate . signal_strength ;
state - > ber = tstate . BER ;
switch ( tstate . tune_state ) {
case TUNE_STATUS_SIGNAL_DVB_OK :
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER ;
break ;
case TUNE_STATUS_STREAM_DETECTED :
2014-08-13 01:50:24 +04:00
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
FE_HAS_VITERBI ;
2014-08-13 01:50:19 +04:00
break ;
case TUNE_STATUS_STREAM_TUNED :
* status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
2014-08-13 01:50:24 +04:00
FE_HAS_LOCK | FE_HAS_VITERBI ;
2014-08-13 01:50:19 +04:00
break ;
default :
* status = TUNE_STATUS_NOT_TUNED ;
}
2014-08-13 01:50:22 +04:00
pr_debug ( " as102: tuner status: 0x%02x, strength %d, per: %d, ber: %d \n " ,
tstate . tune_state , tstate . signal_strength ,
tstate . PER , tstate . BER ) ;
if ( ! ( * status & FE_HAS_LOCK ) ) {
2014-08-13 01:50:19 +04:00
memset ( & state - > demod_stats , 0 , sizeof ( state - > demod_stats ) ) ;
2014-08-13 01:50:22 +04:00
return 0 ;
2014-08-13 01:50:19 +04:00
}
2014-08-13 01:50:22 +04:00
ret = state - > ops - > get_stats ( state - > priv , & state - > demod_stats ) ;
if ( ret < 0 )
memset ( & state - > demod_stats , 0 , sizeof ( state - > demod_stats ) ) ;
2014-08-13 01:50:19 +04:00
return ret ;
}
/*
* Note :
* - in AS102 SNR = MER
* - the SNR will be returned in linear terms , i . e . not in dB
* - the accuracy equals ± 2 dB for a SNR range from 4 dB to 30 dB
* - the accuracy is > 2 dB for SNR values outside this range
*/
static int as102_fe_read_snr ( struct dvb_frontend * fe , u16 * snr )
{
struct as102_state * state = fe - > demodulator_priv ;
* snr = state - > demod_stats . mer ;
return 0 ;
}
static int as102_fe_read_ber ( struct dvb_frontend * fe , u32 * ber )
{
struct as102_state * state = fe - > demodulator_priv ;
* ber = state - > ber ;
return 0 ;
}
static int as102_fe_read_signal_strength ( struct dvb_frontend * fe ,
u16 * strength )
{
struct as102_state * state = fe - > demodulator_priv ;
* strength = ( ( ( 0xffff * 400 ) * state - > signal_strength + 41000 ) * 2 ) ;
return 0 ;
}
static int as102_fe_read_ucblocks ( struct dvb_frontend * fe , u32 * ucblocks )
{
struct as102_state * state = fe - > demodulator_priv ;
if ( state - > demod_stats . has_started )
* ucblocks = state - > demod_stats . bad_frame_count ;
else
* ucblocks = 0 ;
return 0 ;
}
static int as102_fe_ts_bus_ctrl ( struct dvb_frontend * fe , int acquire )
{
struct as102_state * state = fe - > demodulator_priv ;
2014-08-13 01:50:22 +04:00
return state - > ops - > stream_ctrl ( state - > priv , acquire ,
state - > elna_cfg ) ;
2014-08-13 01:50:19 +04:00
}
2014-08-13 04:35:44 +04:00
static void as102_fe_release ( struct dvb_frontend * fe )
{
struct as102_state * state = fe - > demodulator_priv ;
kfree ( state ) ;
}
2016-08-10 00:32:21 +03:00
static const struct dvb_frontend_ops as102_fe_ops = {
2014-08-13 01:50:19 +04:00
. delsys = { SYS_DVBT } ,
. info = {
. name = " Abilis AS102 DVB-T " ,
2018-07-06 01:59:36 +03:00
. frequency_min_hz = 174 * MHz ,
. frequency_max_hz = 862 * MHz ,
. frequency_stepsize_hz = 166667 ,
2014-08-13 01:50:19 +04: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_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK
| 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
} ,
. set_frontend = as102_fe_set_frontend ,
. get_frontend = as102_fe_get_frontend ,
. get_tune_settings = as102_fe_get_tune_settings ,
. read_status = as102_fe_read_status ,
. read_snr = as102_fe_read_snr ,
. read_ber = as102_fe_read_ber ,
. read_signal_strength = as102_fe_read_signal_strength ,
. read_ucblocks = as102_fe_read_ucblocks ,
. ts_bus_ctrl = as102_fe_ts_bus_ctrl ,
2014-08-13 04:35:44 +04:00
. release = as102_fe_release ,
2014-08-13 01:50:19 +04:00
} ;
struct dvb_frontend * as102_attach ( const char * name ,
2014-08-13 01:50:22 +04:00
const struct as102_fe_ops * ops ,
void * priv ,
2014-08-13 01:50:19 +04:00
uint8_t elna_cfg )
{
struct as102_state * state ;
struct dvb_frontend * fe ;
2017-08-28 12:55:16 +03:00
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
2017-08-28 12:46:57 +03:00
if ( ! state )
2014-08-13 01:50:19 +04:00
return NULL ;
2017-08-28 12:46:57 +03:00
2014-08-13 01:50:19 +04:00
fe = & state - > frontend ;
fe - > demodulator_priv = state ;
2014-08-13 01:50:22 +04:00
state - > ops = ops ;
state - > priv = priv ;
2014-08-13 01:50:19 +04:00
state - > elna_cfg = elna_cfg ;
/* init frontend callback ops */
memcpy ( & fe - > ops , & as102_fe_ops , sizeof ( struct dvb_frontend_ops ) ) ;
2018-09-10 15:19:16 +03:00
strscpy ( fe - > ops . info . name , name , sizeof ( fe - > ops . info . name ) ) ;
2014-08-13 01:50:19 +04:00
return fe ;
}
EXPORT_SYMBOL_GPL ( as102_attach ) ;
2014-08-13 01:50:23 +04:00
MODULE_DESCRIPTION ( " as102-fe " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Pierrick Hascoet <pierrick.hascoet@abilis.com> " ) ;