/* * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner * * Copyright (C) 2010 Michael Krufky * * 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-tuner.h" #include "mxl111sf-phy.h" #include "mxl111sf-reg.h" /* debug */ static int mxl111sf_tuner_debug; module_param_named(debug, mxl111sf_tuner_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); #define mxl_dbg(fmt, arg...) \ if (mxl111sf_tuner_debug) \ mxl_printk(KERN_DEBUG, fmt, ##arg) /* ------------------------------------------------------------------------ */ struct mxl111sf_tuner_state { struct mxl111sf_state *mxl_state; struct mxl111sf_tuner_config *cfg; u32 frequency; u32 bandwidth; }; static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state, u8 addr, u8 *data) { return (state->cfg->read_reg) ? state->cfg->read_reg(state->mxl_state, addr, data) : -EINVAL; } static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state, u8 addr, u8 data) { return (state->cfg->write_reg) ? state->cfg->write_reg(state->mxl_state, addr, data) : -EINVAL; } static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_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; } static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state, int onoff) { return (state->cfg->top_master_ctrl) ? state->cfg->top_master_ctrl(state->mxl_state, onoff) : -EINVAL; } /* ------------------------------------------------------------------------ */ static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = { {0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3, DIG_MODEINDEX, _A, _CSF, */ {0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */ {0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */ {0, 0, 0} }; /* ------------------------------------------------------------------------ */ static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq, u8 bw) { u8 filt_bw; /* set channel bandwidth */ switch (bw) { case 0: /* ATSC */ filt_bw = 25; break; case 1: /* QAM */ filt_bw = 69; break; case 6: filt_bw = 21; break; case 7: filt_bw = 42; break; case 8: filt_bw = 63; break; default: err("%s: invalid bandwidth setting!", __func__); return NULL; } /* calculate RF channel */ freq /= 1000000; freq *= 64; #if 0 /* do round */ freq += 0.5; #endif /* set bandwidth */ mxl_phy_tune_rf[0].data = filt_bw; /* set RF */ mxl_phy_tune_rf[1].data = (freq & 0xff); mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff; /* start tune */ return mxl_phy_tune_rf; } static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state) { int ret; u8 ctrl; #if 0 u16 iffcw; u32 if_freq; #endif mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)", state->cfg->invert_spectrum, state->cfg->if_freq); /* set IF polarity */ ctrl = state->cfg->invert_spectrum; ctrl |= state->cfg->if_freq; ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl); if (mxl_fail(ret)) goto fail; #if 0 if_freq /= 1000000; /* do round */ if_freq += 0.5; if (MXL_IF_LO == state->cfg->if_freq) { ctrl = 0x08; iffcw = (u16)(if_freq / (108 * 4096)); } else if (MXL_IF_HI == state->cfg->if_freq) { ctrl = 0x08; iffcw = (u16)(if_freq / (216 * 4096)); } else { ctrl = 0; iffcw = 0; } ctrl |= (iffcw >> 8); #endif ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl); if (mxl_fail(ret)) goto fail; ctrl &= 0xf0; ctrl |= 0x90; ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl); if (mxl_fail(ret)) goto fail; #if 0 ctrl = iffcw & 0x00ff; #endif ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl); mxl_fail(ret); fail: return ret; } static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw) { struct mxl111sf_tuner_state *state = fe->tuner_priv; static struct mxl111sf_reg_ctrl_info *reg_ctrl_array; int ret; u8 mxl_mode; mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw); /* stop tune */ ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0); if (mxl_fail(ret)) goto fail; /* check device mode */ ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode); if (mxl_fail(ret)) goto fail; /* Fill out registers for channel tune */ reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw); if (!reg_ctrl_array) return -EINVAL; ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array); if (mxl_fail(ret)) goto fail; if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) { /* IF tuner mode only */ mxl1x1sf_tuner_top_master_ctrl(state, 0); mxl1x1sf_tuner_top_master_ctrl(state, 1); mxl1x1sf_tuner_set_if_output_freq(state); } ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1); if (mxl_fail(ret)) goto fail; if (state->cfg->ant_hunt) state->cfg->ant_hunt(fe); fail: return ret; } static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state, int *rf_synth_lock, int *ref_synth_lock) { int ret; u8 data; *rf_synth_lock = 0; *ref_synth_lock = 0; ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data); if (mxl_fail(ret)) goto fail; *ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0; *rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0; fail: return ret; } #if 0 static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state, int onoff) { return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG, onoff ? 1 : 0); } #endif /* ------------------------------------------------------------------------ */ static int mxl111sf_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct mxl111sf_tuner_state *state = fe->tuner_priv; int ret; u8 bw; mxl_dbg("()"); if (fe->ops.info.type == FE_ATSC) { switch (params->u.vsb.modulation) { case VSB_8: case VSB_16: bw = 0; /* ATSC */ break; case QAM_64: case QAM_256: bw = 1; /* US CABLE */ break; default: err("%s: modulation not set!", __func__); return -EINVAL; } } else if (fe->ops.info.type == FE_OFDM) { switch (params->u.ofdm.bandwidth) { case BANDWIDTH_6_MHZ: bw = 6; break; case BANDWIDTH_7_MHZ: bw = 7; break; case BANDWIDTH_8_MHZ: bw = 8; break; default: err("%s: bandwidth not set!", __func__); return -EINVAL; } } else { err("%s: modulation type not supported!", __func__); return -EINVAL; } ret = mxl1x1sf_tune_rf(fe, params->frequency, bw); if (mxl_fail(ret)) goto fail; state->frequency = params->frequency; state->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; fail: return ret; } /* ------------------------------------------------------------------------ */ #if 0 static int mxl111sf_tuner_init(struct dvb_frontend *fe) { struct mxl111sf_tuner_state *state = fe->tuner_priv; int ret; /* wake from standby handled by usb driver */ return ret; } static int mxl111sf_tuner_sleep(struct dvb_frontend *fe) { struct mxl111sf_tuner_state *state = fe->tuner_priv; int ret; /* enter standby mode handled by usb driver */ return ret; } #endif /* ------------------------------------------------------------------------ */ static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status) { struct mxl111sf_tuner_state *state = fe->tuner_priv; int rf_locked, ref_locked, ret; *status = 0; ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked); if (mxl_fail(ret)) goto fail; mxl_info("%s%s", rf_locked ? "rf locked " : "", ref_locked ? "ref locked" : ""); if ((rf_locked) || (ref_locked)) *status |= TUNER_STATUS_LOCKED; fail: return ret; } static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength) { struct mxl111sf_tuner_state *state = fe->tuner_priv; u8 val1, val2; int ret; *strength = 0; ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02); if (mxl_fail(ret)) goto fail; ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1); if (mxl_fail(ret)) goto fail; ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2); if (mxl_fail(ret)) goto fail; *strength = val1 | ((val2 & 0x07) << 8); fail: ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00); mxl_fail(ret); return ret; } /* ------------------------------------------------------------------------ */ static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency) { struct mxl111sf_tuner_state *state = fe->tuner_priv; *frequency = state->frequency; return 0; } static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) { struct mxl111sf_tuner_state *state = fe->tuner_priv; *bandwidth = state->bandwidth; return 0; } static int mxl111sf_tuner_release(struct dvb_frontend *fe) { struct mxl111sf_tuner_state *state = fe->tuner_priv; mxl_dbg("()"); kfree(state); fe->tuner_priv = NULL; return 0; } /* ------------------------------------------------------------------------- */ static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = { .info = { .name = "MaxLinear MxL111SF", #if 0 .frequency_min = , .frequency_max = , .frequency_step = , #endif }, #if 0 .init = mxl111sf_tuner_init, .sleep = mxl111sf_tuner_sleep, #endif .set_params = mxl111sf_tuner_set_params, .get_status = mxl111sf_tuner_get_status, .get_rf_strength = mxl111sf_get_rf_strength, .get_frequency = mxl111sf_tuner_get_frequency, .get_bandwidth = mxl111sf_tuner_get_bandwidth, .release = mxl111sf_tuner_release, }; struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, struct mxl111sf_state *mxl_state, struct mxl111sf_tuner_config *cfg) { struct mxl111sf_tuner_state *state = NULL; mxl_dbg("()"); state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL); if (state == NULL) return NULL; state->mxl_state = mxl_state; state->cfg = cfg; memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops, sizeof(struct dvb_tuner_ops)); fe->tuner_priv = state; return fe; } EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach); MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1"); /* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- * Local variables: * c-basic-offset: 8 * End: */