* published by the Free Software Foundation, version 2.
*/
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/i2c.h>
+#include "dvb_math.h"
#include "dvb_frontend.h"
#include "dib7000p.h"
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+static int buggy_sfn_workaround;
+module_param(buggy_sfn_workaround, int, 0644);
+MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)");
+
#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P: "); printk(args); printk("\n"); } } while (0)
struct dib7000p_state {
u16 wbd_ref;
- u8 current_band;
- fe_bandwidth_t current_bandwidth;
+ u8 current_band;
+ u32 current_bandwidth;
struct dibx000_agc_config *current_agc;
u32 timf;
u16 gpio_dir;
u16 gpio_val;
+
+ u8 sfn_workaround_active :1;
};
enum dib7000p_power_mode {
outreg = 0;
fifo_threshold = 1792;
- smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1);
+ smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1);
dprintk( "setting output mode for demod %p to %d",
&state->demod, mode);
if (state->div_force_off) {
dprintk( "diversity combination deactivated - forced by COFDM parameters");
onoff = 0;
- }
+ dib7000p_write_word(state, 207, 0);
+ } else
+ dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0));
+
state->div_state = (u8)onoff;
if (onoff) {
dib7000p_write_word(state, 204, 6);
dib7000p_write_word(state, 205, 16);
/* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
- dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0));
} else {
dib7000p_write_word(state, 204, 1);
dib7000p_write_word(state, 205, 0);
- dib7000p_write_word(state, 207, 0);
}
return 0;
/* offset loop parameters */
dib7000p_write_word(state, 26, 0x6680); // timf(6xxx)
- dib7000p_write_word(state, 29, 0x1273); // isi inh1273 on1073
dib7000p_write_word(state, 32, 0x0003); // pha_off_max(xxx3)
+ dib7000p_write_word(state, 29, 0x1273); // isi
dib7000p_write_word(state, 33, 0x0005); // sfreq(xxx5)
/* P_dvsy_sync_wait */
255, 255, 255, 255, 255, 255};
u32 xtal = state->cfg.bw->xtal_hz / 1000;
- int f_rel = ( (rf_khz + xtal/2) / xtal) * xtal - rf_khz;
+ int f_rel = DIV_ROUND_CLOSEST(rf_khz, xtal) * xtal - rf_khz;
int k;
int coef_re[8],coef_im[8];
int bw_khz = bw;
msleep(45);
/* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
- dib7000p_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3));
+ tmp = (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3);
+ if (state->sfn_workaround_active) {
+ dprintk( "SFN workaround is active");
+ tmp |= (1 << 9);
+ dib7000p_write_word(state, 166, 0x4000); // P_pha3_force_pha_shift
+ } else {
+ dib7000p_write_word(state, 166, 0x0000); // P_pha3_force_pha_shift
+ }
+ dib7000p_write_word(state, 29, tmp);
// never achieved a lock with that bandwidth so far - wait for osc-freq to update
if (state->timf == 0)
fep->inversion = INVERSION_AUTO;
- fep->u.ofdm.bandwidth = state->current_bandwidth;
+ fep->u.ofdm.bandwidth = BANDWIDTH_TO_INDEX(state->current_bandwidth);
switch ((tps >> 8) & 0x3) {
case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
struct dvb_frontend_parameters *fep)
{
struct dib7000p_state *state = fe->demodulator_priv;
- int time;
+ int time, ret;
- state->current_bandwidth = fep->u.ofdm.bandwidth;
- dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->u.ofdm.bandwidth));
+ dib7000p_set_output_mode(state, OUTMODE_HIGH_Z);
+
+ /* maybe the parameter has been changed */
+ state->sfn_workaround_active = buggy_sfn_workaround;
if (fe->ops.tuner_ops.set_params)
fe->ops.tuner_ops.set_params(fe, fep);
dib7000p_get_frontend(fe, fep);
}
- /* make this a config parameter */
- dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO);
+ ret = dib7000p_tune(fe, fep);
- return dib7000p_tune(fe, fep);
+ /* make this a config parameter */
+ dib7000p_set_output_mode(state, state->cfg.output_mode);
+ return ret;
}
static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
*stat |= FE_HAS_VITERBI;
if (lock & 0x0010)
*stat |= FE_HAS_SYNC;
- if (lock & 0x0008)
+ if ((lock & 0x0038) == 0x38)
*stat |= FE_HAS_LOCK;
return 0;
static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr)
{
- *snr = 0x0000;
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 val;
+ s32 signal_mant, signal_exp, noise_mant, noise_exp;
+ u32 result = 0;
+
+ val = dib7000p_read_word(state, 479);
+ noise_mant = (val >> 4) & 0xff;
+ noise_exp = ((val & 0xf) << 2);
+ val = dib7000p_read_word(state, 480);
+ noise_exp += ((val >> 14) & 0x3);
+ if ((noise_exp & 0x20) != 0)
+ noise_exp -= 0x40;
+
+ signal_mant = (val >> 6) & 0xFF;
+ signal_exp = (val & 0x3F);
+ if ((signal_exp & 0x20) != 0)
+ signal_exp -= 0x40;
+
+ if (signal_mant != 0)
+ result = intlog10(2) * 10 * signal_exp + 10 *
+ intlog10(signal_mant);
+ else
+ result = intlog10(2) * 10 * signal_exp - 100;
+
+ if (noise_mant != 0)
+ result -= intlog10(2) * 10 * noise_exp + 10 *
+ intlog10(noise_mant);
+ else
+ result -= intlog10(2) * 10 * noise_exp - 100;
+
+ *snr = result / ((1 << 24) / 10);
return 0;
}
}
EXPORT_SYMBOL(dib7000p_get_i2c_master);
+int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 val = dib7000p_read_word(state, 235) & 0xffef;
+ val |= (onoff & 0x1) << 4;
+ dprintk("PID filter enabled %d", onoff);
+ return dib7000p_write_word(state, 235, val);
+}
+EXPORT_SYMBOL(dib7000p_pid_filter_ctrl);
+
+int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff);
+ return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0);
+}
+EXPORT_SYMBOL(dib7000p_pid_filter);
+
int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[])
{
- struct dib7000p_state st = { .i2c_adap = i2c };
+ struct dib7000p_state *dpst;
int k = 0;
u8 new_addr = 0;
+ dpst = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL);
+ if (!dpst)
+ return -ENOMEM;
+
+ dpst->i2c_adap = i2c;
+
for (k = no_of_demods-1; k >= 0; k--) {
- st.cfg = cfg[k];
+ dpst->cfg = cfg[k];
/* designated i2c address */
new_addr = (0x40 + k) << 1;
- st.i2c_addr = new_addr;
- if (dib7000p_identify(&st) != 0) {
- st.i2c_addr = default_addr;
- if (dib7000p_identify(&st) != 0) {
+ dpst->i2c_addr = new_addr;
+ dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */
+ if (dib7000p_identify(dpst) != 0) {
+ dpst->i2c_addr = default_addr;
+ dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */
+ if (dib7000p_identify(dpst) != 0) {
dprintk("DiB7000P #%d: not identified\n", k);
+ kfree(dpst);
return -EIO;
}
}
/* start diversity to pull_down div_str - just for i2c-enumeration */
- dib7000p_set_output_mode(&st, OUTMODE_DIVERSITY);
+ dib7000p_set_output_mode(dpst, OUTMODE_DIVERSITY);
/* set new i2c address and force divstart */
- dib7000p_write_word(&st, 1285, (new_addr << 2) | 0x2);
+ dib7000p_write_word(dpst, 1285, (new_addr << 2) | 0x2);
dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr);
}
for (k = 0; k < no_of_demods; k++) {
- st.cfg = cfg[k];
- st.i2c_addr = (0x40 + k) << 1;
+ dpst->cfg = cfg[k];
+ dpst->i2c_addr = (0x40 + k) << 1;
// unforce divstr
- dib7000p_write_word(&st, 1285, st.i2c_addr << 2);
+ dib7000p_write_word(dpst, 1285, dpst->i2c_addr << 2);
/* deactivate div - it was just for i2c-enumeration */
- dib7000p_set_output_mode(&st, OUTMODE_HIGH_Z);
+ dib7000p_set_output_mode(dpst, OUTMODE_HIGH_Z);
}
+ kfree(dpst);
return 0;
}
EXPORT_SYMBOL(dib7000p_i2c_enumeration);
st->gpio_val = cfg->gpio_val;
st->gpio_dir = cfg->gpio_dir;
+ /* Ensure the output mode remains at the previous default if it's
+ * not specifically set by the caller.
+ */
+ if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) &&
+ (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
+ st->cfg.output_mode = OUTMODE_MPEG2_FIFO;
+
demod = &st->demod;
demod->demodulator_priv = st;
memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops));
+ dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */
+
if (dib7000p_identify(st) != 0)
goto error;
+ /* FIXME: make sure the dev.parent field is initialized, or else
+ request_firmware() will hit an OOPS (this should be moved somewhere
+ more common) */
+ st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent;
+
dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr);
dib7000p_demod_reset(st);