aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarron Broad <darron@kewl.org>2008-09-13 19:42:16 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-10-12 09:37:06 -0200
commit490c868408bc57a756550148313ac8582fe501ef (patch)
tree09d9b378c9d79aab84fb5bdc43fcf41454e522e3
parenta611d0ca006fe76d88b8dd5ac44f4468293b6760 (diff)
V4L/DVB (9011): S2API: A number of cleanusp from the last 24 months.
I was given these changes by Darron Broad and Igor Liplianin and represent a series of patches they've been making to the cx24116 driver over the last two years. Changes for handling symbolrates >30Ksps Tone handling changes. Diseqc support. Sleep support, shutting down the clocks correctly. Cleanup on ROLL_OFF and PILOT support. *** ST - We need to cleanup the sysclt control, this is abnormal in a demod driver. We should work towards understanding the missing API's and ensure we have them in S2API. *** Signed-off-by: Steven Toth <stoth@linuxtv.org> Signed-off-by: Darron Broad <darron@kewl.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/dvb/frontends/cx24116.c808
1 files changed, 617 insertions, 191 deletions
diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c
index 0ac2c549308..cfb265de522 100644
--- a/drivers/media/dvb/frontends/cx24116.c
+++ b/drivers/media/dvb/frontends/cx24116.c
@@ -2,6 +2,18 @@
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).
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
@@ -18,51 +30,69 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-/*
- * Updates by Darron Broad 2007.
- *
- * March
- * Fixed some bugs.
- * Added diseqc support.
- * Added corrected signal strength support.
- *
- * August
- * Sync with legacy version.
- * Some clean ups.
- */
-/* Updates by Igor Liplianin
- *
- * September, 9th 2008
- * Fixed locking on high symbol rates (>30000).
- */
-
#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 <linux/sysctl.h>
#include "dvb_frontend.h"
#include "cx24116.h"
-/*
- * Fetch firmware in the following manner.
- *
- * #!/bin/sh
- * wget ftp://167.206.143.11/outgoing/Oxford/88x_2_117_24275_1_INF.zip
- * unzip 88x_2_117_24275_1_INF.zip
- * dd if=Driver88/hcw88bda.sys of=dvb-fe-cx24116.fw skip=81768 bs=1 count=32522
- */
+static int debug = 0;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk ("cx24116: " args); \
+ } while (0)
+
#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
#define CX24116_SEARCH_RANGE_KHZ 5000
-/* registers (TO BE COMPLETED) */
-#define CX24116_REG_SIGNAL (0xd5)
+/* 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_QSTATUS (0xbc)
+#define CX24116_REG_QUALITY (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)
/* 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 (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 (0x3f)
+#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 */
@@ -75,21 +105,88 @@
#define CX24116_DISEQC_MINI_A (0)
#define CX24116_DISEQC_MINI_B (1)
-static int debug = 0;
-#define dprintk(args...) \
- do { \
- if (debug) printk ("cx24116: " args); \
- } while (0)
+/* DiSEqC tone burst */
+static int toneburst = 1;
+
+/* debug & toneburst sysctl */
+static struct ctl_table_header *kernel_table_header;
+static ctl_table toneburst_table[] = {
+{
+ .ctl_name = 0,
+ .procname = "toneburst",
+ .data = &toneburst,
+ .maxlen = sizeof(int),
+ .mode = 0666,
+ .child = NULL,
+ .parent = NULL,
+ .proc_handler = &proc_dointvec,
+ .strategy = NULL,
+ .extra1 = NULL,
+ .extra2 = NULL,
+},
+{
+ .ctl_name = 0,
+ .procname = "debug",
+ .data = &debug,
+ .maxlen = sizeof(int),
+ .mode = 0666,
+ .child = NULL,
+ .parent = NULL,
+ .proc_handler = &proc_dointvec,
+ .strategy = NULL,
+ .extra1 = NULL,
+ .extra2 = NULL,
+ },
+ {0},
+};
+static ctl_table cx24116_table[] = {
+{
+ .ctl_name = 0,
+ .procname = "cx24116",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0555,
+ .child = toneburst_table,
+ .parent = NULL,
+ .proc_handler = NULL,
+ .strategy = NULL,
+ .extra1 = NULL,
+ .extra2 = NULL,
+ },
+ {0},
+};
+static ctl_table kernel_table[] = {
+{
+ .ctl_name = CTL_DEV,
+ .procname = "dev",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0555,
+ .child = cx24116_table,
+ .parent = NULL,
+ .proc_handler = NULL,
+ .strategy = NULL,
+ .extra1 = NULL,
+ .extra2 = NULL,
+ },
+ {0},
+};
enum cmds
{
- CMD_INIT_CMD10 = 0x10,
+ CMD_SET_VCO = 0x10,
CMD_TUNEREQUEST = 0x11,
- CMD_INIT_CMD13 = 0x13,
- CMD_INIT_CMD14 = 0x14,
- CMD_SEND_DISEQC = 0x21,
+ CMD_MPEGCONFIG = 0x13,
+ CMD_TUNERINIT = 0x14,
+ CMD_BANDWIDTH = 0x15,
+ CMD_GETAGC = 0x19,
+ CMD_LNBCONFIG = 0x20,
+ CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */
CMD_SET_TONEPRE = 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 */
@@ -101,11 +198,14 @@ struct cx24116_tuning
fe_code_rate_t fec;
fe_modulation_t modulation;
+ fe_pilot_t pilot;
+ fe_rolloff_t rolloff;
/* Demod values */
u8 fec_val;
u8 fec_mask;
u8 inversion_val;
+ u8 rolloff_val;
};
/* Basic commands that are sent to the firmware */
@@ -127,6 +227,7 @@ struct cx24116_state
u8 skip_fw_load;
u8 burst;
+ struct cx24116_cmd dsec_cmd;
};
static int cx24116_writereg(struct cx24116_state* state, int reg, int data)
@@ -233,6 +334,66 @@ static int cx24116_set_inversion(struct cx24116_state* state, fe_spectral_invers
return 0;
}
+/*
+ * modfec (modulation and FEC)
+ * ===========================
+ *
+ * MOD FEC mask/val standard
+ * ---- -------- ----------- --------
+ * QPSK FEC_1_2 0x02 0x02+X DVB-S
+ * QPSK FEC_2_3 0x04 0x02+X DVB-S
+ * QPSK FEC_3_4 0x08 0x02+X DVB-S
+ * QPSK FEC_4_5 0x10 0x02+X DVB-S (?)
+ * QPSK FEC_5_6 0x20 0x02+X DVB-S
+ * QPSK FEC_6_7 0x40 0x02+X DVB-S
+ * QPSK FEC_7_8 0x80 0x02+X DVB-S
+ * QPSK FEC_8_9 0x01 0x02+X DVB-S (?) (NOT SUPPORTED?)
+ * QPSK AUTO 0xff 0x02+X DVB-S
+ *
+ * For DVB-S high byte probably represents FEC
+ * and low byte selects the modulator. The high
+ * byte is search range mask. Bit 5 may turn
+ * on DVB-S and remaining bits represent some
+ * kind of calibration (how/what i do not know).
+ *
+ * Eg.(2/3) szap "Zone Horror"
+ *
+ * mask/val = 0x04, 0x20
+ * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 00000000 | FE_HAS_LOCK
+ *
+ * mask/val = 0x04, 0x30
+ * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 00000000 | FE_HAS_LOCK
+ *
+ * After tuning FECSTATUS contains actual FEC
+ * in use numbered 1 through to 8 for 1/2 .. 2/3 etc
+ *
+ * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only)
+ *
+ * NBC-QPSK FEC_1_2 0x00, 0x04 DVB-S2
+ * NBC-QPSK FEC_3_5 0x00, 0x05 DVB-S2
+ * NBC-QPSK FEC_2_3 0x00, 0x06 DVB-S2
+ * NBC-QPSK FEC_3_4 0x00, 0x07 DVB-S2
+ * NBC-QPSK FEC_4_5 0x00, 0x08 DVB-S2
+ * NBC-QPSK FEC_5_6 0x00, 0x09 DVB-S2
+ * NBC-QPSK FEC_8_9 0x00, 0x0a DVB-S2
+ * NBC-QPSK FEC_9_10 0x00, 0x0b DVB-S2
+ *
+ * NBC-8PSK FEC_3_5 0x00, 0x0c DVB-S2
+ * NBC-8PSK FEC_2_3 0x00, 0x0d DVB-S2
+ * NBC-8PSK FEC_3_4 0x00, 0x0e DVB-S2
+ * NBC-8PSK FEC_5_6 0x00, 0x0f DVB-S2
+ * NBC-8PSK FEC_8_9 0x00, 0x10 DVB-S2
+ * NBC-8PSK FEC_9_10 0x00, 0x11 DVB-S2
+ *
+ * For DVB-S2 low bytes selects both modulator
+ * and FEC. High byte is meaningless here. To
+ * set pilot, bit 6 (0x40) is set. When inspecting
+ * FECSTATUS bit 7 (0x80) represents the pilot
+ * selection whilst not tuned. When tuned, actual FEC
+ * in use is found in FECSTATUS as per above. Pilot
+ * value is reset.
+ */
+
/* A table of modulation, fec and configuration bytes for the demod.
* Not all S2 mmodulation schemes are support and not all rates with
* a scheme are support. Especially, no auto detect when in S2 mode.
@@ -244,15 +405,17 @@ struct cx24116_modfec {
u8 val; /* Passed to the firmware to indicate mode selection */
} CX24116_MODFEC_MODES[] = {
/* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
+
+ /*mod fec mask val */
{ QPSK, FEC_NONE, 0xfe, 0x30 },
- { QPSK, FEC_1_2, 0x02, 0x2e },
- { QPSK, FEC_2_3, 0x04, 0x2f },
- { QPSK, FEC_3_4, 0x08, 0x30 },
- { QPSK, FEC_4_5, 0xfe, 0x30 },
- { QPSK, FEC_5_6, 0x20, 0x31 },
- { QPSK, FEC_6_7, 0xfe, 0x30 },
- { QPSK, FEC_7_8, 0x80, 0x32 },
- { QPSK, FEC_8_9, 0xfe, 0x30 },
+ { QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */
+ { QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */
+ { QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */
+ { QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */
+ { QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */
+ { QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */
+ { QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */
+ { QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */
{ QPSK, FEC_AUTO, 0xfe, 0x30 },
/* NBC-QPSK */
{ NBC_QPSK, FEC_1_2, 0x00, 0x04 },
@@ -268,7 +431,12 @@ struct cx24116_modfec {
{ _8PSK, FEC_2_3, 0x00, 0x0d },
{ _8PSK, FEC_3_4, 0x00, 0x0e },
{ _8PSK, FEC_5_6, 0x00, 0x0f },
+ { _8PSK, FEC_8_9, 0x00, 0x10 },
{ _8PSK, FEC_9_10, 0x00, 0x11 },
+ /*
+ * `val' can be found in the FECSTATUS register when tuning.
+ * FECSTATUS will give the actual FEC in use if tuning was successful.
+ */
};
static int cx24116_lookup_fecmod(struct cx24116_state* state,
@@ -276,6 +444,8 @@ static int cx24116_lookup_fecmod(struct cx24116_state* state,
{
int i, ret = -EOPNOTSUPP;
+ dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f);
+
for(i=0 ; i < sizeof(CX24116_MODFEC_MODES) / sizeof(struct cx24116_modfec) ; i++)
{
if( (m == CX24116_MODFEC_MODES[i].modulation) &&
@@ -292,37 +462,38 @@ static int cx24116_lookup_fecmod(struct cx24116_state* state,
static int cx24116_set_fec(struct cx24116_state* state, fe_modulation_t mod, fe_code_rate_t fec)
{
int ret = 0;
- dprintk("%s()\n", __func__);
+
+ dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec);
ret = cx24116_lookup_fecmod(state, mod, fec);
if(ret < 0)
return ret;
+ state->dnxt.fec = fec;
state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val;
state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask;
- dprintk("%s() fec_val/mask = 0x%02x/0x%02x\n", __func__,
- state->dnxt.fec_val, state->dnxt.fec_mask);
+ dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__,
+ state->dnxt.fec_mask, state->dnxt.fec_val);
return 0;
}
static int cx24116_set_symbolrate(struct cx24116_state* state, u32 rate)
{
- int ret = 0;
+ dprintk("%s(%d)\n", __func__, rate);
- dprintk("%s()\n", __func__);
+ /* check if symbol rate is within limits */
+ if ((rate > state->frontend.ops.info.symbol_rate_max) ||
+ (rate < state->frontend.ops.info.symbol_rate_min)) {
+ dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate);
+ return -EOPNOTSUPP;
+ }
state->dnxt.symbol_rate = rate;
+ dprintk("%s() symbol_rate = %d\n", __func__, rate);
- dprintk("%s() symbol_rate = %d\n", __func__, state->dnxt.symbol_rate);
-
- /* check if symbol rate is within limits */
- if ((state->dnxt.symbol_rate > state->frontend.ops.info.symbol_rate_max) ||
- (state->dnxt.symbol_rate < state->frontend.ops.info.symbol_rate_min))
- ret = -EOPNOTSUPP;
-
- return ret;
+ return 0;
}
static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw);
@@ -392,8 +563,8 @@ static int cx24116_cmd_execute(struct dvb_frontend* fe, struct cx24116_cmd *cmd)
}
/* Start execution and wait for cmd to terminate */
- cx24116_writereg(state, 0x1f, 0x01);
- while( cx24116_readreg(state, 0x1f) )
+ cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01);
+ while( cx24116_readreg(state, CX24116_REG_EXECUTE) )
{
msleep(10);
if(i++ > 64)
@@ -410,7 +581,8 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware
{
struct cx24116_state* state = fe->demodulator_priv;
struct cx24116_cmd cmd;
- int ret;
+ int i, ret;
+ unsigned char vers[4];
dprintk("%s\n", __func__);
dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n"
@@ -427,11 +599,21 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware
/* Begin the firmware load process */
/* Prepare the demod, load the firmware, cleanup after load */
+
+ /* Init PLL */
+ cx24116_writereg(state, 0xE5, 0x00);
cx24116_writereg(state, 0xF1, 0x08);
- cx24116_writereg(state, 0xF2, cx24116_readreg(state, 0xF2) | 0x03);
- cx24116_writereg(state, 0xF3, 0x46);
- cx24116_writereg(state, 0xF9, 0x00);
+ cx24116_writereg(state, 0xF2, 0x13);
+
+ /* Start PLL */
+ cx24116_writereg(state, 0xe0, 0x03);
+ cx24116_writereg(state, 0xe0, 0x00);
+
+ /* Unknown */
+ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
+ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
+ /* Unknown */
cx24116_writereg(state, 0xF0, 0x03);
cx24116_writereg(state, 0xF4, 0x81);
cx24116_writereg(state, 0xF5, 0x00);
@@ -444,8 +626,8 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware
cx24116_writereg(state, 0xF0, 0x00);
cx24116_writereg(state, 0xF8, 0x06);
- /* Firmware CMD 10: Chip config? */
- cmd.args[0x00] = CMD_INIT_CMD10;
+ /* Firmware CMD 10: VCO config */
+ cmd.args[0x00] = CMD_SET_VCO;
cmd.args[0x01] = 0x05;
cmd.args[0x02] = 0xdc;
cmd.args[0x03] = 0xda;
@@ -460,10 +642,10 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware
if (ret != 0)
return ret;
- cx24116_writereg(state, 0x9d, 0x00);
+ cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00);
- /* Firmware CMD 14: Unknown */
- cmd.args[0x00] = CMD_INIT_CMD14;
+ /* Firmware CMD 14: Tuner config */
+ cmd.args[0x00] = CMD_TUNERINIT;
cmd.args[0x01] = 0x00;
cmd.args[0x02] = 0x00;
cmd.len= 0x03;
@@ -473,8 +655,8 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware
cx24116_writereg(state, 0xe5, 0x00);
- /* Firmware CMD 13: Unknown - Firmware config? */
- cmd.args[0x00] = CMD_INIT_CMD13;
+ /* Firmware CMD 13: MPEG config */
+ cmd.args[0x00] = CMD_MPEGCONFIG;
cmd.args[0x01] = 0x01;
cmd.args[0x02] = 0x75;
cmd.args[0x03] = 0x00;
@@ -488,6 +670,19 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware
if (ret != 0)
return ret;
+ /* Firmware CMD 35: Get firmware version */
+ cmd.args[0x00] = CMD_UPDFWVERS;
+ cmd.len= 0x02;
+ for(i=0; i<4; i++) {
+ cmd.args[0x01] = i;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+ vers[i]= cx24116_readreg(state, CX24116_REG_MAILBOX);
+ }
+ printk("%s: FW version %i.%i.%i.%i\n", __func__,
+ vers[0], vers[1], vers[2], vers[3]);
+
return 0;
}
@@ -503,75 +698,98 @@ static int cx24116_read_status(struct dvb_frontend* fe, fe_status_t* status)
{
struct cx24116_state *state = fe->demodulator_priv;
- int lock = cx24116_readreg(state, 0x9d);
+ int lock = cx24116_readreg(state, CX24116_REG_SSTATUS);
dprintk("%s: status = 0x%02x\n", __func__, lock);
*status = 0;
- if (lock & 0x01)
+ if (lock & CX24116_HAS_SIGNAL)
*status |= FE_HAS_SIGNAL;
- if (lock & 0x02)
+ if (lock & CX24116_HAS_CARRIER)
*status |= FE_HAS_CARRIER;
- if (lock & 0x04)
+ if (lock & CX24116_HAS_VITERBI)
*status |= FE_HAS_VITERBI;
- if (lock & 0x08)
+ if (lock & CX24116_HAS_SYNCLOCK)
*status |= FE_HAS_SYNC | FE_HAS_LOCK;
return 0;
}
-/* TODO: Not clear how we do this */
static int cx24116_read_ber(struct dvb_frontend* fe, u32* ber)
{
- //struct cx24116_state *state = fe->demodulator_priv;
+ struct cx24116_state *state = fe->demodulator_priv;
+
dprintk("%s()\n", __func__);
- *ber = 0;
+
+ *ber = ( cx24116_readreg(state, CX24116_REG_BER24) << 24 ) |
+ ( cx24116_readreg(state, CX24116_REG_BER16) << 16 ) |
+ ( cx24116_readreg(state, CX24116_REG_BER8 ) << 8 ) |
+ cx24116_readreg(state, CX24116_REG_BER0 );
return 0;
}
-/* Signal strength (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
+/* TODO Determine function and scale appropriately */
static int cx24116_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
{
struct cx24116_state *state = fe->demodulator_priv;
- u8 strength_reg;
- static const u32 strength_tab[] = { /* 10 x Table (rounded up) */
- 0x00000,0x0199A,0x03333,0x04ccD,0x06667,0x08000,0x0999A,0x0b333,0x0cccD,0x0e667,
- 0x10000,0x1199A,0x13333,0x14ccD,0x16667,0x18000 };
+ struct cx24116_cmd cmd;
+ int ret;
+ u16 sig_reading;
dprintk("%s()\n", __func__);
- strength_reg = cx24116_readreg(state, CX24116_REG_SIGNAL);
+ /* Firmware CMD 19: Get AGC */
+ cmd.args[0x00] = CMD_GETAGC;
+ cmd.len= 0x01;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
- if(strength_reg < 0xa0)
- *signal_strength = strength_tab [ ( strength_reg & 0xf0 ) >> 4 ] +
- ( strength_tab [ ( strength_reg & 0x0f ) ] >> 4 );
- else
- *signal_strength = 0xffff;
+ sig_reading = ( cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK ) |
+ ( cx24116_readreg(state, CX24116_REG_SIGNAL) << 6 );
+ *signal_strength= 0 - sig_reading;
- dprintk("%s: Signal strength (raw / cooked) = (0x%02x / 0x%04x)\n",
- __func__,strength_reg,*signal_strength);
+ dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, sig_reading, *signal_strength);
return 0;
}
-/* TODO: Not clear how we do this */
+/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
static int cx24116_read_snr(struct dvb_frontend* fe, u16* snr)
{
- //struct cx24116_state *state = fe->demodulator_priv;
+ struct cx24116_state *state = fe->demodulator_priv;
+ u8 snr_reading;
+ static const u32 snr_tab[] = { /* 10 x Table (rounded up) */
+ 0x00000,0x0199A,0x03333,0x04ccD,0x06667,
+ 0x08000,0x0999A,0x0b333,0x0cccD,0x0e667,
+ 0x10000,0x1199A,0x13333,0x14ccD,0x16667,0x18000 };
+
dprintk("%s()\n", __func__);
- *snr = 0;
+
+ snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY);
+
+ if(snr_reading >= 0xa0 /* 100% */)
+ *snr = 0xffff;
+ else
+ *snr = snr_tab [ ( snr_reading & 0xf0 ) >> 4 ] +
+ ( snr_tab [ ( snr_reading & 0x0f ) ] >> 4 );
+
+ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+ snr_reading, *snr);
return 0;
}
-/* TODO: Not clear how we do this */
static int cx24116_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
{
- //struct cx24116_state *state = fe->demodulator_priv;
+ struct cx24116_state *state = fe->demodulator_priv;
+
dprintk("%s()\n", __func__);
- *ucblocks = 0;
+
+ *ucblocks = ( cx24116_readreg(state, CX24116_REG_UCB8) << 8 ) |
+ cx24116_readreg(state, CX24116_REG_UCB0);
return 0;
}
@@ -583,6 +801,27 @@ static void cx24116_clone_params(struct dvb_frontend* fe)
memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));
}
+/* Wait for LNB */
+static int cx24116_wait_for_lnb(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ int i;
+
+ dprintk("%s() qstatus = 0x%02x\n", __func__,
+ cx24116_readreg(state, CX24116_REG_QSTATUS));
+
+ /* Wait for up to 300 ms */
+ for(i = 0; i < 30 ; i++) {
+ if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20)
+ return 0;
+ msleep(10);
+ }
+
+ dprintk("%s(): LNB not ready\n", __func__);
+
+ return -ETIMEDOUT; /* -EBUSY ? */
+}
+
static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
{
struct cx24116_cmd cmd;
@@ -594,6 +833,14 @@ static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
return -EINVAL;
}
+ /* Wait for LNB ready */
+ ret = cx24116_wait_for_lnb(fe);
+ if(ret != 0)
+ return ret;
+
+ /* Min delay time after DiSEqC send */
+ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
/* This is always done before the tone is set */
cmd.args[0x00] = CMD_SET_TONEPRE;
cmd.args[0x01] = 0x00;
@@ -619,6 +866,9 @@ static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
}
cmd.len= 0x04;
+ /* Min delay time before DiSEqC send */
+ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
return cx24116_cmd_execute(fe, &cmd);
}
@@ -626,9 +876,39 @@ static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
static int cx24116_diseqc_init(struct dvb_frontend* fe)
{
struct cx24116_state *state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+
+ /* Firmware CMD 20: LNB/DiSEqC config */
+ cmd.args[0x00] = CMD_LNBCONFIG;
+ cmd.args[0x01] = 0x00;
+ cmd.args[0x02] = 0x10;
+ cmd.args[0x03] = 0x00;
+ cmd.args[0x04] = 0x8f;
+ cmd.args[0x05] = 0x28;
+ cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01;
+ cmd.args[0x07] = 0x01;
+ cmd.len= 0x08;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Prepare a DiSEqC command */
+ state->dsec_cmd.args[0x00] = CMD_LNBSEND;
+
+ /* DiSEqC burst */
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A;
+
+ /* Unknown */
+ state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
+ state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
+ state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; /* Continuation flag? */
- /* Default DiSEqC burst state */
- state->burst = CX24116_DISEQC_MINI_A;
+ /* DiSEqC message length */
+ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00;
+
+ /* Command length */
+ state->dsec_cmd.len= CX24116_DISEQC_MSGOFS;
return 0;
}
@@ -637,7 +917,6 @@ static int cx24116_diseqc_init(struct dvb_frontend* fe)
static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *d)
{
struct cx24116_state *state = fe->demodulator_priv;
- struct cx24116_cmd cmd;
int i, ret;
/* Dump DiSEqC message */
@@ -647,82 +926,134 @@ static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_ma
printk("0x%02x", d->msg[i]);
if(++i < d->msg_len)
printk(", ");
- }
- printk(")\n");
+ }
+ printk(") toneburst=%d\n", toneburst);
}
+ /* Validate length */
if(d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS))
return -EINVAL;
- cmd.args[0x00] = CMD_SEND_DISEQC;
- cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
- cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
- cmd.args[CX24116_DISEQC_ARG4_0] = 0x00;
-
/* DiSEqC message */
for (i = 0; i < d->msg_len; i++)
- cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
+ state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
+
+ /* DiSEqC message length */
+ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len;
+
+ /* Command length */
+ state->dsec_cmd.len= CX24116_DISEQC_MSGOFS + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN];
+
+ /* DiSEqC toneburst */
+ if(toneburst == CX24116_DISEQC_MESGCACHE)
+ /* Message is cached */
+ return 0;
+
+ else if(toneburst == CX24116_DISEQC_TONEOFF)
+ /* Message is sent without burst */
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0;
+
+ else if(toneburst == CX24116_DISEQC_TONECACHE) {
+ /*
+ * Message is sent with derived else cached burst
+ *
+ * WRITE PORT GROUP COMMAND 38
+ *
+ * 0/A/A: E0 10 38 F0..F3
+ * 1/B/B: E0 10 38 F4..F7
+ * 2/C/A: E0 10 38 F8..FB
+ * 3/D/B: E0 10 38 FC..FF
+ *
+ * datebyte[3]= 8421:8421
+ * ABCD:WXYZ
+ * CLR :SET
+ *
+ * WX= PORT SELECT 0..3 (X=TONEBURST)
+ * Y = VOLTAGE (0=13V, 1=18V)
+ * Z = BAND (0=LOW, 1=HIGH(22K))
+ */
+ if(d->msg_len >= 4 && d->msg[2] == 0x38)
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = ((d->msg[3] & 4) >> 2);
+ if(debug)
+ dprintk("%s burst=%d\n", __func__, state->dsec_cmd.args[CX24116_DISEQC_BURST]);
+ }
- /* Hack: Derive burst from command else use previous burst */
- if(d->msg_len >= 4 && d->msg[2] == 0x38)
- cmd.args[CX24116_DISEQC_BURST] = (d->msg[3] >> 2) & 1;
- else
- cmd.args[CX24116_DISEQC_BURST] = state->burst;
+ /* Wait for LNB ready */
+ ret = cx24116_wait_for_lnb(fe);
+ if(ret != 0)
+ return ret;
- cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len;
- cmd.len = CX24116_DISEQC_MSGOFS + d->msg_len;
+ /* Wait for voltage/min repeat delay */
+ msleep(100);
- ret = cx24116_cmd_execute(fe, &cmd);
-
- /* Firmware command duration is unknown, so guess...
+ /* Command */
+ ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+ if(ret != 0)
+ return ret;
+ /*
+ * Wait for send
*
* Eutelsat spec:
- * >15ms delay +
- * 13.5ms per byte +
- * >15ms delay +
- * 12.5ms burst +
- * >15ms delay
+ * >15ms delay + (XXX determine if FW does this, see set_tone)
+ * 13.5ms per byte +
+ * >15ms delay +
+ * 12.5ms burst +
+ * >15ms delay (XXX determine if FW does this, see set_tone)
*/
- if(ret == 0)
- msleep( (cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 );
+ msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60) );
- return ret;
+ return 0;
}
/* Send DiSEqC burst */
static int cx24116_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
{
struct cx24116_state *state = fe->demodulator_priv;
- struct cx24116_cmd cmd;
int ret;
- dprintk("%s(%d)\n",__func__,(int)burst);
-
- cmd.args[0x00] = CMD_SEND_DISEQC;
- cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
- cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
- cmd.args[CX24116_DISEQC_ARG4_0] = 0x00;
+ dprintk("%s(%d) toneburst=%d\n",__func__, burst, toneburst);
+ /* DiSEqC burst */
if (burst == SEC_MINI_A)
- cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A;
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A;
else if(burst == SEC_MINI_B)
- cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B;
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B;
else
return -EINVAL;
- /* Cache as previous burst state */
- state->burst= cmd.args[CX24116_DISEQC_BURST];
+ /* DiSEqC toneburst */
+ if(toneburst != CX24116_DISEQC_MESGCACHE)
+ /* Burst is cached */
+ return 0;
- cmd.args[CX24116_DISEQC_MSGLEN] = 0x00;
- cmd.len= CX24116_DISEQC_MSGOFS;
+ /* Burst is to be sent with cached message */
- ret= cx24116_cmd_execute(fe, &cmd);
+ /* Wait for LNB ready */
+ ret = cx24116_wait_for_lnb(fe);
+ if(ret != 0)
+ return ret;
- /* Firmware command duration is unknown, so guess... */
- if(ret == 0)
- msleep(60);
+ /* Wait for voltage/min repeat delay */
+ msleep(100);
- return ret;
+ /* Command */
+ ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+ if(ret != 0)
+ return ret;
+
+ /*
+ * Wait for send
+ *
+ * Eutelsat spec:
+ * >15ms delay + (XXX determine if FW does this, see set_tone)
+ * 13.5ms per byte +
+ * >15ms delay +
+ * 12.5ms burst +
+ * >15ms delay (XXX determine if FW does this, see set_tone)
+ */
+ msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 );
+
+ return 0;
}
static void cx24116_release(struct dvb_frontend* fe)
@@ -730,6 +1061,7 @@ static void cx24116_release(struct dvb_frontend* fe)
struct cx24116_state* state = fe->demodulator_priv;
dprintk("%s\n",__func__);
kfree(state);
+ unregister_sysctl_table(kernel_table_header);
}
static struct dvb_frontend_ops cx24116_ops;
@@ -742,11 +1074,15 @@ struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
dprintk("%s\n",__func__);
+ kernel_table_header = register_sysctl_table(kernel_table);
+ if(!kernel_table_header)
+ goto error1;
+
/* allocate memory for the internal state */
state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL);
if (state == NULL) {
printk("Unable to kmalloc\n");
- goto error;
+ goto error2;
}
/* setup the state */
@@ -759,7 +1095,7 @@ struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
ret = (cx24116_readreg(state, 0xFF) << 8) | cx24116_readreg(state, 0xFE);
if (ret != 0x0501) {
printk("Invalid probe, probably not a CX24116 device\n");
- goto error;
+ goto error3;
}
/* create dvb_frontend */
@@ -767,33 +1103,64 @@ struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
state->frontend.demodulator_priv = state;
return &state->frontend;
-error:
- kfree(state);
-
- return NULL;
+error3: kfree(state);
+error2: unregister_sysctl_table(kernel_table_header);
+error1: return NULL;
}
-
-static int cx24116_get_params(struct dvb_frontend* fe)
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int cx24116_initfe(struct dvb_frontend* fe)
{
- struct cx24116_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *cache = &fe->dtv_property_cache;
+ struct cx24116_state* state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
dprintk("%s()\n",__func__);
- cache->frequency = state->dcur.frequency;
- cache->inversion = state->dcur.inversion;
- cache->modulation = state->dcur.modulation;
- cache->fec_inner = state->dcur.fec;
- cache->symbol_rate = state->dcur.symbol_rate;
+ /* Power on */
+ cx24116_writereg(state, 0xe0, 0);
+ cx24116_writereg(state, 0xe1, 0);
+ cx24116_writereg(state, 0xea, 0);
- return 0;
+ /* Firmware CMD 36: Power config */
+ cmd.args[0x00] = CMD_TUNERSLEEP;
+ cmd.args[0x01] = 0;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if(ret != 0)
+ return ret;
+
+ return cx24116_diseqc_init(fe);
}
-static int cx24116_initfe(struct dvb_frontend* fe)
+/*
+ * Put device to sleep
+ */
+static int cx24116_sleep(struct dvb_frontend* fe)
{
+ struct cx24116_state* state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+
dprintk("%s()\n",__func__);
- return cx24116_diseqc_init(fe);
+ /* Firmware CMD 36: Power config */
+ cmd.args[0x00] = CMD_TUNERSLEEP;
+ cmd.args[0x01] = 1;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if(ret != 0)
+ return ret;
+
+ /* Power off (Shutdown clocks) */
+ cx24116_writereg(state, 0xea, 0xff);
+ cx24116_writereg(state, 0xe1, 1);
+ cx24116_writereg(state, 0xe0, 1);
+
+ return 0;
}
static int cx24116_set_property(struct dvb_frontend *fe, struct dtv_property* tvp)
@@ -817,14 +1184,45 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx24116_cmd cmd;
fe_status_t tunerstat;
- int ret, above30msps;
- u8 retune=4;
+ int i, status, ret, retune = 1;
dprintk("%s()\n",__func__);
state->dnxt.modulation = c->modulation;
state->dnxt.frequency = c->frequency;
+ switch(c->delivery_system) {
+ case SYS_DVBS:
+ dprintk("%s: DVB-S delivery system selected\n",__func__);
+ state->dnxt.pilot = PILOT_OFF;
+ state->dnxt.rolloff = CX24116_ROLLOFF_035;
+ break;
+ case SYS_DVBS2:
+ dprintk("%s: DVB-S2 delivery system selected\n",__func__);
+ if(c->pilot == PILOT_AUTO)
+ retune++;
+ state->dnxt.pilot = c->pilot;
+ switch(c->rolloff) {
+ case ROLLOFF_20:
+ state->dnxt.rolloff_val= CX24116_ROLLOFF_020;
+ break;
+ case ROLLOFF_25:
+ state->dnxt.rolloff_val= CX24116_ROLLOFF_025;
+ break;
+ case ROLLOFF_35:
+ state->dnxt.rolloff_val= CX24116_ROLLOFF_035;
+ break;
+ case ROLLOFF_AUTO:
+ return -EOPNOTSUPP;
+ }
+ state->dnxt.rolloff = c->rolloff;
+ break;
+ default:
+ dprintk("%s: unsupported delivery system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
if ((ret = cx24116_set_inversion(state, c->inversion)) != 0)
return ret;
@@ -837,6 +1235,9 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par
/* discard the 'current' tuning parameters and prepare to tune */
cx24116_clone_params(fe);
+ dprintk("%s: retune = %d\n", __func__, retune);
+ dprintk("%s: rolloff = %d\n", __func__, state->dcur.rolloff);
+ dprintk("%s: pilot = %d\n", __func__, state->dcur.pilot);
dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency);
dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
@@ -844,18 +1245,17 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par
dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__,
state->dcur.inversion, state->dcur.inversion_val);
+ /* This is also done in advise/acquire on HVR4000 but not on LITE */
if (state->config->set_ts_params)
state->config->set_ts_params(fe, 0);
- above30msps = (state->dcur.symbol_rate > 30000000);
-
- if (above30msps){
- cx24116_writereg(state, 0xF9, 0x01);
- cx24116_writereg(state, 0xF3, 0x44);
- } else {
- cx24116_writereg(state, 0xF9, 0x00);
- cx24116_writereg(state, 0xF3, 0x46);
- }
+ /* Set/Reset B/W */
+ cmd.args[0x00] = CMD_BANDWIDTH;
+ cmd.args[0x01] = 0x01;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
/* Prepare a tune request */
cmd.args[0x00] = CMD_TUNEREQUEST;
@@ -875,28 +1275,32 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par
/* Modulation / FEC & Pilot Off */
cmd.args[0x07] = state->dcur.fec_val;
- if (c->pilot == PILOT_ON)
- cmd.args[0x07] |= 0x40;
+ if (state->dcur.pilot == PILOT_ON)
+ cmd.args[0x07] |= CX24116_PILOT;
cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8;
cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff;
cmd.args[0x0a] = 0x00;
cmd.args[0x0b] = 0x00;
- cmd.args[0x0c] = 0x02;
+ cmd.args[0x0c] = state->dcur.rolloff_val;
cmd.args[0x0d] = state->dcur.fec_mask;
- if (above30msps){
+ if (state->dcur.symbol_rate > 30000000) {
cmd.args[0x0e] = 0x04;
cmd.args[0x0f] = 0x00;
cmd.args[0x10] = 0x01;
cmd.args[0x11] = 0x77;
cmd.args[0x12] = 0x36;
+ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44);
+ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01);
} else {
cmd.args[0x0e] = 0x06;
cmd.args[0x0f] = 0x00;
cmd.args[0x10] = 0x00;
cmd.args[0x11] = 0xFA;
cmd.args[0x12] = 0x24;
+ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
+ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
}
cmd.len= 0x13;
@@ -906,29 +1310,47 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par
* the demod does not support autodetect.
*/
do {
- /* Reset status register? */
- cx24116_writereg(state, 0x9d, 0xc1);
+ /* Reset status register */
+ status = cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK;
+ cx24116_writereg(state, CX24116_REG_SSTATUS, status);
/* Tune */
ret = cx24116_cmd_execute(fe, &cmd);
if( ret != 0 )
break;
- /* The hardware can take time to lock, wait a while */
- msleep(500);
-
- cx24116_read_status(fe, &tunerstat);
- if(tunerstat & FE_HAS_SIGNAL) {
- if(tunerstat & FE_HAS_SYNC)
- /* Tuned */
- break;
- else if(c->pilot == PILOT_AUTO)
- /* Toggle pilot bit */
- cmd.args[0x07] ^= 0x40;
+ /*
+ * Wait for up to 500 ms before retrying
+ *
+ * If we are able to tune then generally it occurs within 100ms.
+ * If it takes longer, try a different toneburst setting.
+ */
+ for(i = 0; i < 50 ; i++) {
+ cx24116_read_status(fe, &tunerstat);
+ status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
+ if(status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
+ dprintk("%s: Tuned\n",__func__);
+ goto tuned;
+ }
+ msleep(10);
}
+
+ dprintk("%s: Not tuned\n",__func__);
+
+ /* Toggle pilot bit when in auto-pilot */
+ if(state->dcur.pilot == PILOT_AUTO)
+ cmd.args[0x07] ^= CX24116_PILOT;
}
while(--retune);
+tuned: /* Set/Reset B/W */
+ cmd.args[0x00] = CMD_BANDWIDTH;
+ cmd.args[0x01] = 0x00;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
return ret;
}
@@ -953,6 +1375,7 @@ static struct dvb_frontend_ops cx24116_ops = {
.release = cx24116_release,
.init = cx24116_initfe,
+ .sleep = cx24116_sleep,
.read_status = cx24116_read_status,
.read_ber = cx24116_read_ber,
.read_signal_strength = cx24116_read_signal_strength,
@@ -971,6 +1394,9 @@ static struct dvb_frontend_ops cx24116_ops = {
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+module_param(toneburst, int, 0644);
+MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, 2=MESSAGE CACHE (default:1)");
+
MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware");
MODULE_AUTHOR("Steven Toth");
MODULE_LICENSE("GPL");