/*
Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com>
Copyright (C) 2006-2007 Georg Acher
Copyright (C) 2007-2008 Darron Broad
March 2007
Fixed some bugs.
Added diseqc support.
Added corrected signal strength support.
August 2007
Sync with legacy version.
Some clean ups.
Copyright (C) 2008 Igor Liplianin
September, 9th 2008
Fixed locking on high symbol rates (>30000).
Implement MPEG initialization parameter.
January, 17th 2009
Fill set_voltage with actually control voltage code.
Correct set tone to not affect voltage.
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/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <media/dvb_frontend.h>
#include "cx24116.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
#define dprintk(args...) \
do { \
if (debug) \
printk(KERN_INFO "cx24116: " args); \
} while (0)
#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
#define CX24116_SEARCH_RANGE_KHZ 5000
/* known registers */
#define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */
#define CX24116_REG_EXECUTE (0x1f) /* execute command */
#define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */
#define CX24116_REG_RESET (0x20) /* reset status > 0 */
#define CX24116_REG_SIGNAL (0x9e) /* signal low */
#define CX24116_REG_SSTATUS (0x9d) /* signal high / status */
#define CX24116_REG_QUALITY8 (0xa3)
#define CX24116_REG_QSTATUS (0xbc)
#define CX24116_REG_QUALITY0 (0xd5)
#define CX24116_REG_BER0 (0xc9)
#define CX24116_REG_BER8 (0xc8)
#define CX24116_REG_BER16 (0xc7)
#define CX24116_REG_BER24 (0xc6)
#define CX24116_REG_UCB0 (0xcb)
#define CX24116_REG_UCB8 (0xca)
#define CX24116_REG_CLKDIV (0xf3)
#define CX24116_REG_RATEDIV (0xf9)
/* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */
#define CX24116_REG_FECSTATUS (0x9c)
/* FECSTATUS bits */
/* mask to determine configured fec (not tuned) or actual fec (tuned) */
#define CX24116_FEC_FECMASK (0x1f)
/* Select DVB-S demodulator, else DVB-S2 */
#define CX24116_FEC_DVBS (0x20)
#define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */
/* Pilot mode requested when tuning else always reset when tuned */
#define CX24116_FEC_PILOT (0x80)
/* arg buffer size */
#define CX24116_ARGLEN (0x1e)
/* rolloff */
#define CX24116_ROLLOFF_020 (0x00)
#define CX24116_ROLLOFF_025 (0x01)
#define CX24116_ROLLOFF_035 (0x02)
/* pilot bit */
#define CX24116_PILOT_OFF (0x00)
#define CX24116_PILOT_ON (0x40)
/* signal status */
#define CX24116_HAS_SIGNAL (0x01)
#define CX24116_HAS_CARRIER (0x02)
#define CX24116_HAS_VITERBI (0x04)
#define CX24116_HAS_SYNCLOCK (0x08)
#define CX24116_HAS_UNKNOWN1 (0x10)
#define CX24116_HAS_UNKNOWN2 (0x20)
#define CX24116_STATUS_MASK (0x0f)
#define CX24116_SIGNAL_MASK (0xc0)
#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */
#define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */
#define CX24116_DISEQC_MESGCACHE (2) /* message cached */
/* arg offset for DiSEqC */
#define CX24116_DISEQC_BURST (1)
#define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */
#define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */
#define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */
#define CX24116_DISEQC_MSGLEN (5)
#define CX24116_DISEQC_MSGOFS (6)
/* DiSEqC burst */
#define CX24116_DISEQC_MINI_A (0)
#define CX24116_DISEQC_MINI_B (1)
/* DiSEqC tone burst */
static int toneburst = 1;
module_param(toneburst, int, 0644);
MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, "\
"2=MESSAGE CACHE (default:1)");
/* SNR measurements */
static int esno_snr;
module_param(esno_snr, int, 0644);
MODULE_PARM_DESC(esno_snr, "SNR return units, 0=PERCENTAGE 0-100, "\
"1=ESNO(db * 10) (default:0)");
enum cmds {
CMD_SET_VCO = 0x10,
CMD_TUNEREQUEST = 0x11,
CMD_MPEGCONFIG = 0x13,
CMD_TUNERINIT = 0x14,
CMD_BANDWIDTH = 0x15,
CMD_GETAGC = 0x19,
CMD_LNBCONFIG = 0x20,
CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */
CMD_LNBDCLEVEL = 0x22,
CMD_SET_TONE = 0x23,
CMD_UPDFWVERS = 0x35,
CMD_TUNERSLEEP = 0x36,
CMD_AGCCONTROL = 0x3b, /* Unknown */
};
/* The Demod/Tuner can't easily provide these, we cache them */
struct cx24116_tuning {
u32 frequency;
u32 symbol_rate;
enum fe_spectral_inversion inversion;
enum fe_code_rate fec;
enum fe_delivery_system delsys;
enum fe_modulation modulation;
enum fe_pilot pilot;
enum fe_rolloff rolloff;
/* Demod values */
u8 fec_val;
u8 fec_mask;
u8 inversion_val;
u8 pilot_val;
u8 rolloff_val;
};
/* Basic commands that are sent to the firmware */
struct cx24116_cmd {
u8 len;
u8 args[CX24116_ARGLEN];
};
struct cx24116_state {
struct i2c_adapter *i2c;
const struct cx24116_config *config;
struct dvb_frontend frontend;
struct cx24116_tuning dcur;
struct cx24116_tuning dnxt;
u8 skip_fw_load;
u8 burst;
struct cx24116_cmd dsec_cmd;
};
static int cx24116_writereg(struct cx24116_state *state, int reg, int data)
{
u8 buf[] = { reg, data };
struct i2c_msg msg = { .addr = state->config->demod_address,
.flags = 0, .buf = buf, .len = 2 };
int err;
if (debug > 1)
printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n",
__func__, reg, data);
err = i2c_transfer(state->i2c, &msg, 1);
if (err != 1) {
printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n",
__func__, err, reg, data);
return -EREMOTEIO;
}
return 0;
}
/* Bulk byte writes to a single I2C address, for 32k firmware load */
static int cx24116_writeregN(struct cx24116_state *state, int reg,
const u8 *data, u16 len)
{
int ret;
struct i2c_msg msg;
u8 *buf;
buf = kmalloc(len + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
*(buf) = reg;
memcpy(buf + 1, data, len);
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.buf = buf;
msg.len = len + 1;
if (debug > 1)
printk(KERN_INFO "cx24116: %s: write regN 0x%02x, len = %d\n",
__func__, reg, len);
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1) {
printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n",
__func__, ret, reg);
ret = -EREMOTEIO;
}
kfree(buf);
return ret;
}
static int cx24116_readreg(struct cx24116_state *state, u8 reg)
{
int ret;
u8 b0[] = { reg };
u8 b1[] = { 0 };
struct i2c_msg msg[] = {
{ .addr = state->config->demod_address, .flags = 0,
.buf = b0, .len = 1 },
{ .addr = state->config->demod_address, .flags = I2C_M_RD,
.buf = b1, .len = 1 }
};
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2) {
printk(KERN_ERR "%s: reg=0x%x (error=%d)\n",
__func__, reg, ret);
return ret;
}
if (debug > 1)
printk(KERN_INFO "cx24116: read reg 0x%02x, value 0x%02x\n",
reg, b1[0]);
return b1[0];
}
static int cx24116_set_inversion(struct cx24116_state *state,
enum fe_spectral_inversion inversion)
{
dprintk("%s(%d)\n", __func__, inversion);
switch (inversion) {
case INVERSION_OFF:
state->dnxt.inversion_val = 0x00;
break;
case INVERSION_ON:
state->dnxt.inversion_val = 0x04;
break;
case INVERSION_AUTO:
state->dnxt.inversion_val = 0x0C;
break;
default:
return -EINVAL;
}
state->dnxt.inversion = inversion;
return 0;
}
/*
* modfec (modulation and FEC)
* =
|