7581e61d8d
Now that this field is deprecated, and core generates it for DVBv3 calls, remove it from the drivers. It also adds .delsys on the few drivers where this were missed. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
612 lines
15 KiB
C
612 lines
15 KiB
C
/*
|
|
* 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 02139, 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
|
|
int mxl1x1sf_demod_get_tps_modulation(struct mxl111sf_demod_state *state,
|
|
fe_modulation_t *modulation)
|
|
{
|
|
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:
|
|
*modulation = QPSK;
|
|
break;
|
|
case 1:
|
|
*modulation = QAM_16;
|
|
break;
|
|
case 2:
|
|
*modulation = QAM_64;
|
|
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);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe)
|
|
{
|
|
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) {
|
|
ret = fe->ops.tuner_ops.set_params(fe);
|
|
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;
|
|
}
|
|
|
|
#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. */
|
|
#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))
|
|
#else
|
|
#define CALCULATE_BER(avg_errors, count) 0
|
|
#define CALCULATE_SNR(data) 0
|
|
#endif
|
|
|
|
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;
|
|
fe_modulation_t modulation;
|
|
u16 snr;
|
|
|
|
mxl111sf_demod_calc_snr(state, &snr);
|
|
mxl1x1sf_demod_get_tps_modulation(state, &modulation);
|
|
|
|
switch (modulation) {
|
|
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;
|
|
}
|
|
|
|
static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe)
|
|
{
|
|
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
|
|
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)
|
|
fe->ops.tuner_ops.get_bandwidth(fe, &p->bandwidth_hz);
|
|
if (fe->ops.tuner_ops.get_frequency)
|
|
fe->ops.tuner_ops.get_frequency(fe, &p->frequency);
|
|
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);
|
|
mxl1x1sf_demod_get_tps_guard_fft_mode(state,
|
|
&p->transmission_mode);
|
|
mxl1x1sf_demod_get_tps_guard_interval(state,
|
|
&p->guard_interval);
|
|
mxl1x1sf_demod_get_tps_hierarchy(state,
|
|
&p->hierarchy);
|
|
|
|
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 = {
|
|
.delsys = { SYS_DVBT },
|
|
.info = {
|
|
.name = "MaxLinear MxL111SF DVB-T demodulator",
|
|
.frequency_min = 177000000,
|
|
.frequency_max = 858000000,
|
|
.frequency_stepsize = 166666,
|
|
.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
|
|
.set_frontend = mxl111sf_demod_set_frontend,
|
|
.get_frontend = mxl111sf_demod_get_frontend,
|
|
.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:
|
|
*/
|