/*
Conexant cx24117/cx24132 - Dual DVBS/S2 Satellite demod/tuner driver
Copyright (C) 2013 Luis Alves <ljalvs@gmail.com>
July, 6th 2013
First release based on cx24116 driver by:
Steven Toth and Georg Acher, Darron Broad, Igor Liplianin
Cards currently supported:
TBS6980 - Dual DVBS/S2 PCIe card
TBS6981 - Dual DVBS/S2 PCIe card
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 "tuner-i2c.h"
#include "dvb_frontend.h"
#include "cx24117.h"
#define CX24117_DEFAULT_FIRMWARE "dvb-fe-cx24117.fw"
#define CX24117_SEARCH_RANGE_KHZ 5000
/* known registers */
#define CX24117_REG_COMMAND (0x00) /* command buffer */
#define CX24117_REG_EXECUTE (0x1f) /* execute command */
#define CX24117_REG_FREQ3_0 (0x34) /* frequency */
#define CX24117_REG_FREQ2_0 (0x35)
#define CX24117_REG_FREQ1_0 (0x36)
#define CX24117_REG_STATE0 (0x39)
#define CX24117_REG_SSTATUS0 (0x3a) /* demod0 signal high / status */
#define CX24117_REG_SIGNAL0 (0x3b)
#define CX24117_REG_FREQ5_0 (0x3c) /* +-freq */
#define CX24117_REG_FREQ6_0 (0x3d)
#define CX24117_REG_SRATE2_0 (0x3e) /* +- 1000 * srate */
#define CX24117_REG_SRATE1_0 (0x3f)
#define CX24117_REG_QUALITY2_0 (0x40)
#define CX24117_REG_QUALITY1_0 (0x41)
#define CX24117_REG_BER4_0 (0x47)
#define CX24117_REG_BER3_0 (0x48)
#define CX24117_REG_BER2_0 (0x49)
#define CX24117_REG_BER1_0 (0x4a)
#define CX24117_REG_DVBS_UCB2_0 (0x4b)
#define CX24117_REG_DVBS_UCB1_0 (0x4c)
#define CX24117_REG_DVBS2_UCB2_0 (0x50)
#define CX24117_REG_DVBS2_UCB1_0 (0x51)
#define CX24117_REG_QSTATUS0 (0x93)
#define CX24117_REG_CLKDIV0 (0xe6)
#define CX24117_REG_RATEDIV0 (0xf0)
#define CX24117_REG_FREQ3_1 (0x55) /* frequency */
#define CX24117_REG_FREQ2_1 (0x56)
#define CX24117_REG_FREQ1_1 (0x57)
#define CX24117_REG_STATE1 (0x5a)
#define CX24117_REG_SSTATUS1 (0x5b) /* demod1 signal high / status */
#define CX24117_REG_SIGNAL1 (0x5c)
#define CX24117_REG_FREQ5_1 (0x5d) /* +- freq */
#define CX24117_REG_FREQ4_1 (0x5e)
#define CX24117_REG_SRATE2_1 (0x5f)
#define CX24117_REG_SRATE1_1 (0x60)
#define CX24117_REG_QUALITY2_1 (0x61)
#define CX24117_REG_QUALITY1_1 (0x62)
#define CX24117_REG_BER4_1 (0x68)
#define CX24117_REG_BER3_1 (0x69)
#define CX24117_REG_BER2_1 (0x6a)
#define CX24117_REG_BER1_1 (0x6b)
#define CX24117_REG_DVBS_UCB2_1 (0x6c)
#define CX24117_REG_DVBS_UCB1_1 (0x6d)
#define CX24117_REG_DVBS2_UCB2_1 (0x71)
#define CX24117_REG_DVBS2_UCB1_1 (0x72)
#define CX24117_REG_QSTATUS1 (0x9f)
#define CX24117_REG_CLKDIV1 (0xe7)
#define CX24117_REG_RATEDIV1 (0xf1)
/* arg buffer size */
#define CX24117_ARGLEN (0x1e)
/* rolloff */
#define CX24117_ROLLOFF_020 (0x00)
#define CX24117_ROLLOFF_025 (0x01)
#define CX24117_ROLLOFF_035 (0x02)
/* pilot bit */
#define CX24117_PILOT_OFF (0x00)
#define CX24117_PILOT_ON (0x40)
#define CX24117_PILOT_AUTO (0x80)
/* signal status */
#define CX24117_HAS_SIGNAL (0x01)
#define CX24117_HAS_CARRIER (0x02)
#define CX24117_HAS_VITERBI (0x04)
#define CX24117_HAS_SYNCLOCK (0x08)
#define CX24117_STATUS_MASK (0x0f)
#define CX24117_SIGNAL_MASK (0xc0)
/* arg offset for DiSEqC */
#define CX24117_DISEQC_DEMOD (1)
#define CX24117_DISEQC_BURST (2)
#define CX24117_DISEQC_ARG3_2 (3) /* unknown value=2 */
#define CX24117_DISEQC_ARG4_0 (4) /* unknown value=0 */
#define CX24117_DISEQC_ARG5_0 (5) /* unknown value=0 */
#define CX24117_DISEQC_MSGLEN (6)
#define CX24117_DISEQC_MSGOFS (7)
/* DiSEqC burst */
#define CX24117_DISEQC_MINI_A (0)
#define CX24117_DISEQC_MINI_B (1)
#define CX24117_PNE (0) /* 0 disabled / 2 enabled */
#define CX24117_OCC (1) /* 0 disabled / 1 enabled */
enum cmds {
CMD_SET_VCO = 0x10,
CMD_TUNEREQUEST = 0x11,
CMD_MPEGCONFIG = 0x13,
CMD_TUNERINIT = 0x14,
CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */
CMD_LNBDCLEVEL = 0x22,
CMD_SET_TONE = 0x23,
CMD_UPDFWVERS = 0x35,
CMD_TUNERSLEEP = 0x36,
};
static LIST_HEAD(hybrid_tuner_instance_list);
static DEFINE_MUTEX(cx24117_list_mutex);
/* The Demod/Tuner can't easily provide these, we cache them */
struct cx24117_tuning {
u32 frequency;
u32 symbol_rate;
fe_spectral_inversion_t inversion;
fe_code_rate_t fec;
fe_delivery_system_t delsys;
fe_modulation_t modulation;
fe_pilot_t pilot;
fe_rolloff_t 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 cx24117_cmd {
u8 len;
u8 args[CX24117_ARGLEN];
};
/* common to both fe's */
struct cx24117_priv {
u8 demod_address;
struct i2c_adapter *i2c;
u8 skip_fw_load;
struct mutex fe_lock;
/* Used for sharing this struct between demods */
struct tuner_i2c_props i2c_props;
struct list_head hybrid_tuner_instance_list;
};
/* one per each fe */
struct cx24117_state {
struct cx24117_priv *priv;
struct dvb_frontend frontend;
struct cx24117_tuning dcur;
struct cx24117_tuning dnxt;
struct cx24117_cmd dsec_cmd;
int demod;
};
/* modfec (modulation and FEC) lookup table */
/* Check cx24116.c for a detailed description of each field */
static struct cx24117_modfec {
fe_delivery_system_t delivery_system;
fe_modulation_t modulation;
fe_code_rate_t fec;
u8 mask; /* In DVBS mode this is used to autodetect */
u8 val; /* Passed to the firmware to indicate mode selection */
} cx24117_modfec_modes[] = {
/* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
/*mod fec mask val */
{ SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 },
{ SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */
{ SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */
{ SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */
{ SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */
{ SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */
{ SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */
{ SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */
{ SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */
{ SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 },
/* NBC-QPSK */
{ SYS_DVBS2, QPSK, FEC_NONE, 0x00, 0x00 },
{ SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 },
{ SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 },
{ SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 },
{ SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 },
{ SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 },
{ SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 },
{ SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a },
{ SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b },
{ SYS_DVBS2, QPSK, FEC_AUTO, 0x00, 0x00 },
/* 8PSK */
{ SYS_DVBS2, PSK_8, FEC_NONE, 0x00, 0x00 },
{ SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c },
{ SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d },
{ SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e },
{ SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f },
{ SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 },
{ SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 },
{ SYS_DVBS2, PSK_8, FEC_AUTO, 0x00, 0x00 },
/*
* 'val' can be found in the FECSTATUS register when tuning.
* FE