/*
* Driver for
* Samsung S5H1420 and
* PnpNetwork PN1010 QPSK Demodulator
*
* Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net>
* Copyright (C) 2005-8 Patrick Boettcher <pb@linuxtv.org>
*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <asm/div64.h>
#include <linux/i2c.h>
#include "dvb_frontend.h"
#include "s5h1420.h"
#include "s5h1420_priv.h"
#define TONE_FREQ 22000
struct s5h1420_state {
struct i2c_adapter* i2c;
const struct s5h1420_config* config;
struct dvb_frontend frontend;
struct i2c_adapter tuner_i2c_adapter;
u8 CON_1_val;
u8 postlocked:1;
u32 fclk;
u32 tunedfreq;
fe_code_rate_t fec_inner;
u32 symbol_rate;
/* FIXME: ugly workaround for flexcop's incapable i2c-controller
* it does not support repeated-start, workaround: write addr-1
* and then read
*/
u8 shadow[255];
};
static u32 s5h1420_getsymbolrate(struct s5h1420_state* state);
static int s5h1420_get_tune_settings(struct dvb_frontend* fe,
struct dvb_frontend_tune_settings* fesettings);
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debugging");
#define dprintk(x...) do { \
if (debug) \
printk(KERN_DEBUG "S5H1420: " x); \
} while (0)
static u8 s5h1420_readreg(struct s5h1420_state *state, u8 reg)
{
int ret;
u8 b[2];
struct i2c_msg msg[] = {
{ .addr = state->config->demod_address, .flags = 0, .buf = b, .len = 2 },
{ .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 },
{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = 1 },
};
b[0] = (reg - 1) & 0xff;
b[1] = state->shadow[(reg - 1) & 0xff];
if (state->config->repeated_start_workaround) {
ret = i2c_transfer(state->i2c, msg, 3);
if (ret != 3)
return ret;
} else {
ret = i2c_transfer(state->i2c, &msg[1], 2);
if (ret != 2)
return ret;
}
/* dprintk("rd(%02x): %02x %02x\n", state->config->demod_address, reg, b[0]); */
return b[0];
}
static int s5h1420_writereg (struct s5h1420_state* state, u8 reg, u8 data)
{
u8 buf[] = { reg, data };
struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
int err;
/* dprintk("wr(%02x): %02x %02x\n", state->config->demod_address, reg, data); */
err = i2c_transfer(state->i2c, &msg, 1);
if (err != 1) {
dprintk("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data);
return -EREMOTEIO;
}
state->shadow[reg] = data;
return 0;
}
static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
{
struct s5h1420_state* state = fe->demodulator_priv;
dprintk("enter %s\n", __func__);
switch(voltage) {
case SEC_VOLTAGE_13:
s5h1420_writereg(state, 0x3c,
(s5h1420_readreg(state, 0x3c) & 0xfe) | 0x02);
break;
case SEC_VOLTAGE_18:
s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) | 0x03);
break;
case SEC_VOLTAGE_OFF:
s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) & 0xfd);
break;
}
dprintk("leave %s\n", __func__);
return 0;
}
static int