/*
* Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM
*
* Copyright (C) 2008, 2009, 2010 Michael Krufky <mkrufky@linuxtv.org>
*
* LGDT3304 support by Jarod Wilson <jarod@redhat.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 <asm/div64.h>
#include <linux/dvb/frontend.h>
#include <linux/slab.h>
#include "dvb_math.h"
#include "lgdt3305.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))");
#define DBG_INFO 1
#define DBG_REG 2
#define lg_printk(kern, fmt, arg...) \
printk(kern "%s: " fmt, __func__, ##arg)
#define lg_info(fmt, arg...) printk(KERN_INFO "lgdt3305: " fmt, ##arg)
#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg)
#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg)
#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \
lg_printk(KERN_DEBUG, fmt, ##arg)
#define lg_reg(fmt, arg...) if (debug & DBG_REG) \
lg_printk(KERN_DEBUG, fmt, ##arg)
#define lg_fail(ret) \
({ \
int __ret; \
__ret = (ret < 0); \
if (__ret) \
lg_err("error %d on line %d\n", ret, __LINE__); \
__ret; \
})
struct lgdt3305_state {
struct i2c_adapter *i2c_adap;
const struct lgdt3305_config *cfg;
struct dvb_frontend frontend;
fe_modulation_t current_modulation;
u32 current_frequency;
u32 snr;
};
/* ------------------------------------------------------------------------ */
/* FIXME: verify & document the LGDT3304 registers */
#define LGDT3305_GEN_CTRL_1 0x0000
#define LGDT3305_GEN_CTRL_2 0x0001
#define LGDT3305_GEN_CTRL_3 0x0002
#define LGDT3305_GEN_STATUS 0x0003
#define LGDT3305_GEN_CONTROL 0x0007
#define LGDT3305_GEN_CTRL_4 0x000a
#define LGDT3305_DGTL_AGC_REF_1 0x0012
#define LGDT3305_DGTL_AGC_REF_2 0x0013
#define LGDT3305_CR_CTR_FREQ_1 0x0106
#define LGDT3305_CR_CTR_FREQ_2 0x0107
#define LGDT3305_CR_CTR_FREQ_3 0x0108
#define LGDT3305_CR_CTR_FREQ_4 0x0109
#define LGDT3305_CR_MSE_1 0x011b
#define LGDT3305_CR_MSE_2 0x011c
#define LGDT3305_CR_LOCK_STATUS 0x011d
#define LGDT3305_CR_CTRL_7 0x0126
#define LGDT3305_AGC_POWER_REF_1 0x0300
#define LGDT3305_AGC_POWER_REF_2 0x0301
#define LGDT3305_AGC_DELAY_PT_1 0x0302
#define LGDT3305_AGC_DELAY_PT_2 0x0303
#define LGDT3305_RFAGC_LOOP_FLTR_BW_1 0x0306
#define LGDT3305_RFAGC_LOOP_FLTR_BW_2 0x0307
#define LGDT3305_IFBW_1 0x0308
#define LGDT3305_IFBW_2 0x0309
#define LGDT3305_AGC_CTRL_1 0x030c
#define LGDT3305_AGC_CTRL_4 0x0314
#define LGDT3305_EQ_MSE_1 0x0413
#define LGDT3305_EQ_MSE_2 0x0414
#define LGDT3305_EQ_MSE_3 0x0415
#define LGDT3305_PT_MSE_1 0x0417
#define LGDT3305_PT_MSE_2 0x0418
#define LGDT3305_PT_MSE_3 0x0419
#define LGDT3305_FEC_BLOCK_CTRL 0x0504
#define LGDT3305_FEC_LOCK_STATUS 0x050a
#define LGDT3305_FEC_PKT_ERR_1 0x050c
#define LGDT3305_FEC_PKT_ERR_2 0x050d
#define LGDT3305_TP_CTRL_1 0x050e
#define LGDT3305_BERT_PERIOD 0x0801
#define LGDT3305_BERT_ERROR_COUNT_1 0x080a
#define LGDT3305_BERT_ERROR_COUNT_2 0x080b
#define LGDT3305_BERT_ERROR_COUNT_3 0x080c
#define LGDT3305_BERT_ERROR_COUNT_4 0x080d
static int lgdt3305_write_reg(struct lgdt3305_state *state, u16 reg, u8 val)
{
int ret;
u8 buf[] = { reg >> 8, reg & 0xff, val };
struct i2c_msg msg = {
.addr = state->cfg->i2c_addr, .flags = 0,
.buf = buf, .len = 3,
};
lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val);
ret = i2c_transfer(state->i2c_adap, &msg, 1);
if (ret != 1) {
lg_err("error (addr %02x %02x <- %02x, err = %i)\n",
msg.buf[0], msg.buf[1], msg.buf[2], ret);
if (ret < 0)
return ret;
else
return -EREMOTEIO;
}
return 0;
}
static int lgdt3305_read_reg(struct lgdt3305_state *state, u16 reg, u8 *val