/*
* Driver for DiBcom DiB3000MC/P-demodulator.
*
* Copyright (C) 2004-7 DiBcom (http://www.dibcom.fr/)
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* This code is partially based on the previous dib3000mc.c .
*
* 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, version 2.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include "dvb_frontend.h"
#include "dib3000mc.h"
static int debug;
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 "DiB3000MC/P:"); printk(args); printk("\n"); } } while (0)
struct dib3000mc_state {
struct dvb_frontend demod;
struct dib3000mc_config *cfg;
u8 i2c_addr;
struct i2c_adapter *i2c_adap;
struct dibx000_i2c_master i2c_master;
u32 timf;
u32 current_bandwidth;
u16 dev_id;
u8 sfn_workaround_active :1;
};
static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg)
{
u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
u8 rb[2];
struct i2c_msg msg[2] = {
{ .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
{ .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
};
if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
dprintk("i2c read error on %d\n",reg);
return (rb[0] << 8) | rb[1];
}
static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val)
{
u8 b[4] = {
(reg >> 8) & 0xff, reg & 0xff,
(val >> 8) & 0xff, val & 0xff,
};
struct i2c_msg msg = {
.addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
};
return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
}
static int dib3000mc_identify(struct dib3000mc_state *state)
{
u16 value;
if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) {
dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value);
return -EREMOTEIO;
}
value = dib3000mc_read_word(state, 1026);
if (value != 0x3001 && value != 0x3002) {
dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value);
return -EREMOTEIO;
}
state->dev_id = value;
dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id);
return 0;
}
static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw, u8 update_offset)
{
u32 timf;
if (state->timf == 0) {
timf = 1384402; // default value for 8MHz
if (update_offset)
msleep(200); // first time we do an update
} else
timf = state->timf;
timf *= (bw / 1000);
if (update_offset) {
s16 tim_offs = dib3000mc_read_word(state, 416);
if (tim_offs & 0x2000)
tim_offs -= 0x4000;
if (nfft == TRANSMISSION_MODE_2K)
tim_offs *= 4;
timf += tim_offs;
state->timf = timf / (bw / 1000);
}
dprintk("timf: %d\n", timf);
dib3000mc_write_word(state, 23, (u16) (timf >> 16));
dib3000mc_write_word(state, 24, (u16) (timf ) & 0xffff);
return 0;
}
static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state)
{
u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb;
if (state->cfg->pwm3_inversion) {
reg_51 = (2 << 14)