2011-09-08 07:44:15 +01:00
/*
* mxl111sf - demod . c - driver for the MaxLinear MXL111SF DVB - T demodulator
*
* Copyright ( C ) 2010 Michael Krufky < mkrufky @ kernellabs . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "mxl111sf-demod.h"
# include "mxl111sf-reg.h"
/* debug */
static int mxl111sf_demod_debug ;
module_param_named ( debug , mxl111sf_demod_debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " set debugging level (1=info (or-able)). " ) ;
# define mxl_dbg(fmt, arg...) \
if ( mxl111sf_demod_debug ) \
mxl_printk ( KERN_DEBUG , fmt , # # arg )
/* ------------------------------------------------------------------------ */
struct mxl111sf_demod_state {
struct mxl111sf_state * mxl_state ;
struct mxl111sf_demod_config * cfg ;
struct dvb_frontend fe ;
} ;
/* ------------------------------------------------------------------------ */
static int mxl111sf_demod_read_reg ( struct mxl111sf_demod_state * state ,
u8 addr , u8 * data )
{
return ( state - > cfg - > read_reg ) ?
state - > cfg - > read_reg ( state - > mxl_state , addr , data ) :
- EINVAL ;
}
static int mxl111sf_demod_write_reg ( struct mxl111sf_demod_state * state ,
u8 addr , u8 data )
{
return ( state - > cfg - > write_reg ) ?
state - > cfg - > write_reg ( state - > mxl_state , addr , data ) :
- EINVAL ;
}
static
int mxl111sf_demod_program_regs ( struct mxl111sf_demod_state * state ,
struct mxl111sf_reg_ctrl_info * ctrl_reg_info )
{
return ( state - > cfg - > program_regs ) ?
state - > cfg - > program_regs ( state - > mxl_state , ctrl_reg_info ) :
- EINVAL ;
}
/* ------------------------------------------------------------------------ */
/* TPS */
static
int mxl1x1sf_demod_get_tps_code_rate ( struct mxl111sf_demod_state * state ,
fe_code_rate_t * code_rate )
{
u8 val ;
int ret = mxl111sf_demod_read_reg ( state , V6_CODE_RATE_TPS_REG , & val ) ;
/* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */
if ( mxl_fail ( ret ) )
goto fail ;
switch ( val & V6_CODE_RATE_TPS_MASK ) {
case 0 :
* code_rate = FEC_1_2 ;
break ;
case 1 :
* code_rate = FEC_2_3 ;
break ;
case 2 :
* code_rate = FEC_3_4 ;
break ;
case 3 :
* code_rate = FEC_5_6 ;
break ;
case 4 :
* code_rate = FEC_7_8 ;
break ;
}
fail :
return ret ;
}
static
2011-12-26 15:55:51 -03:00
int mxl1x1sf_demod_get_tps_modulation ( struct mxl111sf_demod_state * state ,
fe_modulation_t * modulation )
2011-09-08 07:44:15 +01:00
{
u8 val ;
int ret = mxl111sf_demod_read_reg ( state , V6_MODORDER_TPS_REG , & val ) ;
/* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */
if ( mxl_fail ( ret ) )
goto fail ;
switch ( ( val & V6_PARAM_CONSTELLATION_MASK ) > > 4 ) {
case 0 :
2011-12-26 15:55:51 -03:00
* modulation = QPSK ;
2011-09-08 07:44:15 +01:00
break ;
case 1 :
2011-12-26 15:55:51 -03:00
* modulation = QAM_16 ;
2011-09-08 07:44:15 +01:00
break ;
case 2 :
2011-12-26 15:55:51 -03:00
* modulation = QAM_64 ;
2011-09-08 07:44:15 +01:00
break ;
}
fail :
return ret ;
}
static
int mxl1x1sf_demod_get_tps_guard_fft_mode ( struct mxl111sf_demod_state * state ,
fe_transmit_mode_t * fft_mode )
{
u8 val ;
int ret = mxl111sf_demod_read_reg ( state , V6_MODE_TPS_REG , & val ) ;
/* FFT Mode, 00:2K, 01:8K, 10:4K */
if ( mxl_fail ( ret ) )
goto fail ;
switch ( ( val & V6_PARAM_FFT_MODE_MASK ) > > 2 ) {
case 0 :
* fft_mode = TRANSMISSION_MODE_2K ;
break ;
case 1 :
* fft_mode = TRANSMISSION_MODE_8K ;
break ;
case 2 :
* fft_mode = TRANSMISSION_MODE_4K ;
break ;
}
fail :
return ret ;
}
static
int mxl1x1sf_demod_get_tps_guard_interval ( struct mxl111sf_demod_state * state ,
fe_guard_interval_t * guard )
{
u8 val ;
int ret = mxl111sf_demod_read_reg ( state , V6_CP_TPS_REG , & val ) ;
/* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */
if ( mxl_fail ( ret ) )
goto fail ;
switch ( ( val & V6_PARAM_GI_MASK ) > > 4 ) {
case 0 :
* guard = GUARD_INTERVAL_1_32 ;
break ;
case 1 :
* guard = GUARD_INTERVAL_1_16 ;
break ;
case 2 :
* guard = GUARD_INTERVAL_1_8 ;
break ;
case 3 :
* guard = GUARD_INTERVAL_1_4 ;
break ;
}
fail :
return ret ;
}
static
int mxl1x1sf_demod_get_tps_hierarchy ( struct mxl111sf_demod_state * state ,
fe_hierarchy_t * hierarchy )
{
u8 val ;
int ret = mxl111sf_demod_read_reg ( state , V6_TPS_HIERACHY_REG , & val ) ;
/* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */
if ( mxl_fail ( ret ) )
goto fail ;
switch ( ( val & V6_TPS_HIERARCHY_INFO_MASK ) > > 6 ) {
case 0 :
* hierarchy = HIERARCHY_NONE ;
break ;
case 1 :
* hierarchy = HIERARCHY_1 ;
break ;
case 2 :
* hierarchy = HIERARCHY_2 ;
break ;
case 3 :
* hierarchy = HIERARCHY_4 ;
break ;
}
fail :
return ret ;
}
/* ------------------------------------------------------------------------ */
/* LOCKS */
static
int mxl1x1sf_demod_get_sync_lock_status ( struct mxl111sf_demod_state * state ,
int * sync_lock )
{
u8 val = 0 ;
int ret = mxl111sf_demod_read_reg ( state , V6_SYNC_LOCK_REG , & val ) ;
if ( mxl_fail ( ret ) )
goto fail ;
* sync_lock = ( val & SYNC_LOCK_MASK ) > > 4 ;
fail :
return ret ;
}
static
int mxl1x1sf_demod_get_rs_lock_status ( struct mxl111sf_demod_state * state ,
int * rs_lock )
{
u8 val = 0 ;
int ret = mxl111sf_demod_read_reg ( state , V6_RS_LOCK_DET_REG , & val ) ;
if ( mxl_fail ( ret ) )
goto fail ;
* rs_lock = ( val & RS_LOCK_DET_MASK ) > > 3 ;
fail :
return ret ;
}
static
int mxl1x1sf_demod_get_tps_lock_status ( struct mxl111sf_demod_state * state ,
int * tps_lock )
{
u8 val = 0 ;
int ret = mxl111sf_demod_read_reg ( state , V6_TPS_LOCK_REG , & val ) ;
if ( mxl_fail ( ret ) )
goto fail ;
* tps_lock = ( val & V6_PARAM_TPS_LOCK_MASK ) > > 6 ;
fail :
return ret ;
}
static
int mxl1x1sf_demod_get_fec_lock_status ( struct mxl111sf_demod_state * state ,
int * fec_lock )
{
u8 val = 0 ;
int ret = mxl111sf_demod_read_reg ( state , V6_IRQ_STATUS_REG , & val ) ;
if ( mxl_fail ( ret ) )
goto fail ;
* fec_lock = ( val & IRQ_MASK_FEC_LOCK ) > > 4 ;
fail :
return ret ;
}
#if 0
static
int mxl1x1sf_demod_get_cp_lock_status ( struct mxl111sf_demod_state * state ,
int * cp_lock )
{
u8 val = 0 ;
int ret = mxl111sf_demod_read_reg ( state , V6_CP_LOCK_DET_REG , & val ) ;
if ( mxl_fail ( ret ) )
goto fail ;
* cp_lock = ( val & V6_CP_LOCK_DET_MASK ) > > 2 ;
fail :
return ret ;
}
# endif
static int mxl1x1sf_demod_reset_irq_status ( struct mxl111sf_demod_state * state )
{
return mxl111sf_demod_write_reg ( state , 0x0e , 0xff ) ;
}
/* ------------------------------------------------------------------------ */
2011-12-26 15:55:51 -03:00
static int mxl111sf_demod_set_frontend ( struct dvb_frontend * fe )
2011-09-08 07:44:15 +01:00
{
struct mxl111sf_demod_state * state = fe - > demodulator_priv ;
int ret = 0 ;
struct mxl111sf_reg_ctrl_info phy_pll_patch [ ] = {
{ 0x00 , 0xff , 0x01 } , /* change page to 1 */
{ 0x40 , 0xff , 0x05 } ,
{ 0x40 , 0xff , 0x01 } ,
{ 0x41 , 0xff , 0xca } ,
{ 0x41 , 0xff , 0xc0 } ,
{ 0x00 , 0xff , 0x00 } , /* change page to 0 */
{ 0 , 0 , 0 }
} ;
mxl_dbg ( " () " ) ;
if ( fe - > ops . tuner_ops . set_params ) {
2011-12-24 12:24:33 -03:00
ret = fe - > ops . tuner_ops . set_params ( fe ) ;
2011-09-08 07:44:15 +01:00
if ( mxl_fail ( ret ) )
goto fail ;
msleep ( 50 ) ;
}
ret = mxl111sf_demod_program_regs ( state , phy_pll_patch ) ;
mxl_fail ( ret ) ;
msleep ( 50 ) ;
ret = mxl1x1sf_demod_reset_irq_status ( state ) ;
mxl_fail ( ret ) ;
msleep ( 100 ) ;
fail :
return ret ;
}
/* ------------------------------------------------------------------------ */
#if 0
/* resets TS Packet error count */
/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */
static
int mxl1x1sf_demod_reset_packet_error_count ( struct mxl111sf_demod_state * state )
{
struct mxl111sf_reg_ctrl_info reset_per_count [ ] = {
{ 0x20 , 0x01 , 0x01 } ,
{ 0x20 , 0x01 , 0x00 } ,
{ 0 , 0 , 0 }
} ;
return mxl111sf_demod_program_regs ( state , reset_per_count ) ;
}
# endif
/* returns TS Packet error count */
/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */
static int mxl111sf_demod_read_ucblocks ( struct dvb_frontend * fe , u32 * ucblocks )
{
struct mxl111sf_demod_state * state = fe - > demodulator_priv ;
u32 fec_per_count , fec_per_scale ;
u8 val ;
int ret ;
* ucblocks = 0 ;
/* FEC_PER_COUNT Register */
ret = mxl111sf_demod_read_reg ( state , V6_FEC_PER_COUNT_REG , & val ) ;
if ( mxl_fail ( ret ) )
goto fail ;
fec_per_count = val ;
/* FEC_PER_SCALE Register */
ret = mxl111sf_demod_read_reg ( state , V6_FEC_PER_SCALE_REG , & val ) ;
if ( mxl_fail ( ret ) )
goto fail ;
val & = V6_FEC_PER_SCALE_MASK ;
val * = 4 ;
fec_per_scale = 1 < < val ;
fec_per_count * = fec_per_scale ;
* ucblocks = fec_per_count ;
fail :
return ret ;
}
2011-10-20 15:42:34 +01:00
# ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS
/* FIXME: leaving this enabled breaks the build on some architectures,
* and we shouldn ' t have any floating point math in the kernel , anyway .
*
* These macros need to be re - written , but it ' s harmless to simply
* return zero for now . */
2011-09-08 07:44:15 +01:00
# define CALCULATE_BER(avg_errors, count) \
( ( u32 ) ( avg_errors * 4 ) / ( count * 64 * 188 * 8 ) )
# define CALCULATE_SNR(data) \
( ( u32 ) ( ( 10 * ( u32 ) data / 64 ) - 2.5 ) )
2011-10-20 15:42:34 +01:00
# else
# define CALCULATE_BER(avg_errors, count) 0
# define CALCULATE_SNR(data) 0
# endif
2011-09-08 07:44:15 +01:00
static int mxl111sf_demod_read_ber ( struct dvb_frontend * fe , u32 * ber )
{
struct mxl111sf_demod_state * state = fe - > demodulator_priv ;
u8 val1 , val2 , val3 ;
int ret ;
* ber = 0 ;
ret = mxl111sf_demod_read_reg ( state , V6_RS_AVG_ERRORS_LSB_REG , & val1 ) ;
if ( mxl_fail ( ret ) )
goto fail ;
ret = mxl111sf_demod_read_reg ( state , V6_RS_AVG_ERRORS_MSB_REG , & val2 ) ;
if ( mxl_fail ( ret ) )
goto fail ;
ret = mxl111sf_demod_read_reg ( state , V6_N_ACCUMULATE_REG , & val3 ) ;
if ( mxl_fail ( ret ) )
goto fail ;
* ber = CALCULATE_BER ( ( val1 | ( val2 < < 8 ) ) , val3 ) ;
fail :
return ret ;
}
static int mxl111sf_demod_calc_snr ( struct mxl111sf_demod_state * state ,
u16 * snr )
{
u8 val1 , val2 ;
int ret ;
* snr = 0 ;
ret = mxl111sf_demod_read_reg ( state , V6_SNR_RB_LSB_REG , & val1 ) ;
if ( mxl_fail ( ret ) )
goto fail ;
ret = mxl111sf_demod_read_reg ( state , V6_SNR_RB_MSB_REG , & val2 ) ;
if ( mxl_fail ( ret ) )
goto fail ;
* snr = CALCULATE_SNR ( val1 | ( ( val2 & 0x03 ) < < 8 ) ) ;
fail :
return ret ;
}
static int mxl111sf_demod_read_snr ( struct dvb_frontend * fe , u16 * snr )
{
struct mxl111sf_demod_state * state = fe - > demodulator_priv ;
int ret = mxl111sf_demod_calc_snr ( state , snr ) ;
if ( mxl_fail ( ret ) )
goto fail ;
* snr / = 10 ; /* 0.1 dB */
fail :
return ret ;
}
static int mxl111sf_demod_read_status ( struct dvb_frontend * fe ,
fe_status_t * status )
{
struct mxl111sf_demod_state * state = fe - > demodulator_priv ;
int ret , locked , cr_lock , sync_lock , fec_lock ;
* status = 0 ;
ret = mxl1x1sf_demod_get_rs_lock_status ( state , & locked ) ;
if ( mxl_fail ( ret ) )
goto fail ;
ret = mxl1x1sf_demod_get_tps_lock_status ( state , & cr_lock ) ;
if ( mxl_fail ( ret ) )
goto fail ;
ret = mxl1x1sf_demod_get_sync_lock_status ( state , & sync_lock ) ;
if ( mxl_fail ( ret ) )
goto fail ;
ret = mxl1x1sf_demod_get_fec_lock_status ( state , & fec_lock ) ;
if ( mxl_fail ( ret ) )
goto fail ;
if ( locked )
* status | = FE_HAS_SIGNAL ;
if ( cr_lock )
* status | = FE_HAS_CARRIER ;
if ( sync_lock )
* status | = FE_HAS_SYNC ;
if ( fec_lock ) /* false positives? */
* status | = FE_HAS_VITERBI ;
if ( ( locked ) & & ( cr_lock ) & & ( sync_lock ) )
* status | = FE_HAS_LOCK ;
fail :
return ret ;
}
static int mxl111sf_demod_read_signal_strength ( struct dvb_frontend * fe ,
u16 * signal_strength )
{
struct mxl111sf_demod_state * state = fe - > demodulator_priv ;
2011-12-26 15:55:51 -03:00
fe_modulation_t modulation ;
2011-09-08 07:44:15 +01:00
u16 snr ;
mxl111sf_demod_calc_snr ( state , & snr ) ;
2011-12-26 15:55:51 -03:00
mxl1x1sf_demod_get_tps_modulation ( state , & modulation ) ;
2011-09-08 07:44:15 +01:00
2011-12-26 15:55:51 -03:00
switch ( modulation ) {
2011-09-08 07:44:15 +01:00
case QPSK :
* signal_strength = ( snr > = 1300 ) ?
min ( 65535 , snr * 44 ) : snr * 38 ;
break ;
case QAM_16 :
* signal_strength = ( snr > = 1500 ) ?
min ( 65535 , snr * 38 ) : snr * 33 ;
break ;
case QAM_64 :
* signal_strength = ( snr > = 2000 ) ?
min ( 65535 , snr * 29 ) : snr * 25 ;
break ;
default :
* signal_strength = 0 ;
return - EINVAL ;
}
return 0 ;
}
[media] dvb: don't require a parameter for get_frontend
Just like set_frontend, use the dvb cache properties for get_frontend.
This is more consistent, as both functions are now symetric. Also,
at the places get_frontend is called, it makes sense to update the
cache.
Most of this patch were generated by this small perl script:
while (<>) { $file .= $_; }
if ($file =~ m/\.get_frontend\s*=\s*([\d\w_]+)/) {
my $get = $1;
$file =~ s/($get)(\s*\([^\,\)]+)\,\s*struct\s+dtv_frontend_properties\s*\*\s*([_\d\w]+)\)\s*\{/\1\2)\n{\n\tstruct dtv_frontend_properties *\3 = &fe->dtv_property_cache;/g;
}
print $file;
Of course, the changes at dvb_frontend.[ch] were made by hand,
as well as the changes on a few other places, where get_frontend()
is called internally inside the driver.
On some places, get_frontend() were just a void function. Those
occurrences were removed, as the DVB core handles such cases.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-12-30 11:30:21 -03:00
static int mxl111sf_demod_get_frontend ( struct dvb_frontend * fe )
2011-09-08 07:44:15 +01:00
{
[media] dvb: don't require a parameter for get_frontend
Just like set_frontend, use the dvb cache properties for get_frontend.
This is more consistent, as both functions are now symetric. Also,
at the places get_frontend is called, it makes sense to update the
cache.
Most of this patch were generated by this small perl script:
while (<>) { $file .= $_; }
if ($file =~ m/\.get_frontend\s*=\s*([\d\w_]+)/) {
my $get = $1;
$file =~ s/($get)(\s*\([^\,\)]+)\,\s*struct\s+dtv_frontend_properties\s*\*\s*([_\d\w]+)\)\s*\{/\1\2)\n{\n\tstruct dtv_frontend_properties *\3 = &fe->dtv_property_cache;/g;
}
print $file;
Of course, the changes at dvb_frontend.[ch] were made by hand,
as well as the changes on a few other places, where get_frontend()
is called internally inside the driver.
On some places, get_frontend() were just a void function. Those
occurrences were removed, as the DVB core handles such cases.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-12-30 11:30:21 -03:00
struct dtv_frontend_properties * p = & fe - > dtv_property_cache ;
2011-09-08 07:44:15 +01:00
struct mxl111sf_demod_state * state = fe - > demodulator_priv ;
mxl_dbg ( " () " ) ;
#if 0
p - > inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF ;
# endif
if ( fe - > ops . tuner_ops . get_bandwidth )
2011-12-26 15:55:51 -03:00
fe - > ops . tuner_ops . get_bandwidth ( fe , & p - > bandwidth_hz ) ;
2011-09-08 07:44:15 +01:00
if ( fe - > ops . tuner_ops . get_frequency )
fe - > ops . tuner_ops . get_frequency ( fe , & p - > frequency ) ;
2011-12-26 15:55:51 -03:00
mxl1x1sf_demod_get_tps_code_rate ( state , & p - > code_rate_HP ) ;
mxl1x1sf_demod_get_tps_code_rate ( state , & p - > code_rate_LP ) ;
mxl1x1sf_demod_get_tps_modulation ( state , & p - > modulation ) ;
2011-09-08 07:44:15 +01:00
mxl1x1sf_demod_get_tps_guard_fft_mode ( state ,
2011-12-26 15:55:51 -03:00
& p - > transmission_mode ) ;
2011-09-08 07:44:15 +01:00
mxl1x1sf_demod_get_tps_guard_interval ( state ,
2011-12-26 15:55:51 -03:00
& p - > guard_interval ) ;
2011-09-08 07:44:15 +01:00
mxl1x1sf_demod_get_tps_hierarchy ( state ,
2011-12-26 15:55:51 -03:00
& p - > hierarchy ) ;
2011-09-08 07:44:15 +01:00
return 0 ;
}
static
int mxl111sf_demod_get_tune_settings ( struct dvb_frontend * fe ,
struct dvb_frontend_tune_settings * tune )
{
tune - > min_delay_ms = 1000 ;
return 0 ;
}
static void mxl111sf_demod_release ( struct dvb_frontend * fe )
{
struct mxl111sf_demod_state * state = fe - > demodulator_priv ;
mxl_dbg ( " () " ) ;
kfree ( state ) ;
fe - > demodulator_priv = NULL ;
}
static struct dvb_frontend_ops mxl111sf_demod_ops = {
2011-12-26 15:55:51 -03:00
. delsys = { SYS_DVBT } ,
2011-09-08 07:44:15 +01:00
. info = {
2011-10-23 10:46:13 +01:00
. name = " MaxLinear MxL111SF DVB-T demodulator " ,
. frequency_min = 177000000 ,
. frequency_max = 858000000 ,
. frequency_stepsize = 166666 ,
2011-09-08 07:44:15 +01: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 |
FE_CAN_QAM_AUTO |
FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
} ,
. release = mxl111sf_demod_release ,
#if 0
. init = mxl111sf_init ,
. i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl ,
# endif
2011-12-26 15:55:51 -03:00
. set_frontend = mxl111sf_demod_set_frontend ,
. get_frontend = mxl111sf_demod_get_frontend ,
2011-09-08 07:44:15 +01:00
. get_tune_settings = mxl111sf_demod_get_tune_settings ,
. read_status = mxl111sf_demod_read_status ,
. read_signal_strength = mxl111sf_demod_read_signal_strength ,
. read_ber = mxl111sf_demod_read_ber ,
. read_snr = mxl111sf_demod_read_snr ,
. read_ucblocks = mxl111sf_demod_read_ucblocks ,
} ;
struct dvb_frontend * mxl111sf_demod_attach ( struct mxl111sf_state * mxl_state ,
struct mxl111sf_demod_config * cfg )
{
struct mxl111sf_demod_state * state = NULL ;
mxl_dbg ( " () " ) ;
state = kzalloc ( sizeof ( struct mxl111sf_demod_state ) , GFP_KERNEL ) ;
if ( state = = NULL )
return NULL ;
state - > mxl_state = mxl_state ;
state - > cfg = cfg ;
memcpy ( & state - > fe . ops , & mxl111sf_demod_ops ,
sizeof ( struct dvb_frontend_ops ) ) ;
state - > fe . demodulator_priv = state ;
return & state - > fe ;
}
EXPORT_SYMBOL_GPL ( mxl111sf_demod_attach ) ;
MODULE_DESCRIPTION ( " MaxLinear MxL111SF DVB-T demodulator driver " ) ;
MODULE_AUTHOR ( " Michael Krufky <mkrufky@kernellabs.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( " 0.1 " ) ;
/*
* Local variables :
* c - basic - offset : 8
* End :
*/