Re: More on the cx24123

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 





Yeasah Pell wrote:
/ I've done some more testing, and I feel much more confident about my
/>/ theory on the cx24123 bands. From my testing, I believe that the tuner's />/ VCO #8 has a center frequency around 2300MHz, and is only useful when />/ using the /4 divider (so there are effectively only 9 band selects, not />/ 10). I think the datasheet is essentially correct in terms of showing />/ individual band ranges, it just mixes up the assignments. />/ />/ Also, I did confirm that I can tune low symbol rate signals using the />/ original spec'd VGA offset values from the spec if the demodulator />/ sample gain is set correctly. />/ />/ I did also do some experimentation to determine the behavior of the />/ demodulator's frequency search capability, and it definitely does do />/ some of that, and can be controlled with the range and initial frequency />/ parameters, but I haven't quite nailed down the exact behavior (for one />/ thing, it looks more like +/-4MHz instead of the advertised +/-10MHz). />/ The final thing I want to figure out before I'm satisifed with the />/ performance of the driver is the interaction between the demodulator's />/ frequency search and the linux dvb software zigzag. />/ />/ I haven't gotten any comments on any of this so far -- is anybody />/ actually interested in these improvements? I have no idea if this is a />/ common card or not.
/
Could you send a patch with your changes to the list, so that we could
test them? I'm particularly interested in the changes to the demodulator
sample gain. I have tried different settings of that, but without
changing the VGA offset values the card did not tune well to signals
with low symbol rates.

Thanks,
Vadim Catana


Sure, sorry about the delay, I've been extra busy the last few days. My patches are of course based on your patch that adds diseqc and all that other goodness, so I'm not sure if it would be more useful to have a patch relative to your last patch or the current file in the main repository. I'll attach both.

I never did come to a good resolution with the swzigzag issue, so I left the frequency search parameters at their defaults for now.

/Yeasah
--- v4l-dvb/linux/drivers/media/dvb/frontends/cx24123.c.vadim	2006-03-18 23:57:14.000000000 -0500
+++ v4l-dvb/linux/drivers/media/dvb/frontends/cx24123.c	2006-03-24 13:13:27.000000000 -0500
@@ -31,6 +31,7 @@
 
 #define XTAL 10111000
 
+static int force_band;
 static int debug;
 #define dprintk(args...) \
 	do { \
@@ -45,8 +46,6 @@
 
 	struct dvb_frontend frontend;
 
-	u32 lastber;
-	u16 snr;
 	u8  lnbreg;
 
 	/* Some PLL specifics for tuning */
@@ -74,25 +73,23 @@
 	{
 		.symbolrate_low		= 1000000,
 		.symbolrate_high	= 4999999,
-		/* the specs recommend other values for VGA offsets,
-		   but tests show they are wrong */
-		.VGAprogdata		= (2 << 18) | (0x180 << 9) | 0x1e0,
-		.VCAprogdata		= (4 << 18) | (0x07 << 9) | 0x07,
-		.FILTune		= 0x280 /* 0.41 V */
+		.VGAprogdata		= (1 << 19) | (0x1f8 << 9) | 0x1f8,
+		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x07,
+		.FILTune		= 0x27f /* 0.41 V */
 	},
 	{
 		.symbolrate_low		=  5000000,
 		.symbolrate_high	= 14999999,
-		.VGAprogdata		= (2 << 18) | (0x180 << 9) | 0x1e0,
-		.VCAprogdata		= (4 << 18) | (0x07 << 9) | 0x1f,
+		.VGAprogdata		= (1 << 19) | (0x180 << 9) | 0x1e0,
+		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x1f,
 		.FILTune		= 0x317 /* 0.90 V */
 	},
 	{
 		.symbolrate_low		= 15000000,
 		.symbolrate_high	= 45000000,
-		.VGAprogdata		= (2 << 18) | (0x100 << 9) | 0x180,
-		.VCAprogdata		= (4 << 18) | (0x07 << 9) | 0x3f,
-		.FILTune		= 0x146 /* 2.70 V */
+		.VGAprogdata		= (1 << 19) | (0x100 << 9) | 0x180,
+		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x3f,
+		.FILTune		= 0x145 /* 2.70 V */
 	},
 };
 
@@ -111,64 +108,69 @@
 {
 	{
 		.freq_low	= 950000,
-		.freq_high	= 1018999,
-		.VCOdivider	= 4,
-		.progdata	= (0 << 18) | (0 << 9) | 0x40,
-	},
-	{
-		.freq_low	= 1019000,
 		.freq_high	= 1074999,
 		.VCOdivider	= 4,
-		.progdata	= (0 << 18) | (0 << 9) | 0x80,
+		.progdata	= (0 << 19) | (0 << 9) | 0x40,
 	},
 	{
 		.freq_low	= 1075000,
-		.freq_high	= 1227999,
-		.VCOdivider	= 2,
-		.progdata	= (0 << 18) | (1 << 9) | 0x01,
+		.freq_high	= 1177999,
+		.VCOdivider	= 4,
+		.progdata	= (0 << 19) | (0 << 9) | 0x80,
 	},
 	{
-		.freq_low	= 1228000,
-		.freq_high	= 1349999,
+		.freq_low	= 1178000,
+		.freq_high	= 1295999,
 		.VCOdivider	= 2,
-		.progdata	= (0 << 18) | (1 << 9) | 0x02,
+		.progdata	= (0 << 19) | (1 << 9) | 0x01,
 	},
 	{
-		.freq_low	= 1350000,
-		.freq_high	= 1481999,
+		.freq_low	= 1296000,
+		.freq_high	= 1431999,
 		.VCOdivider	= 2,
-		.progdata	= (0 << 18) | (1 << 9) | 0x04,
+		.progdata	= (0 << 19) | (1 << 9) | 0x02,
 	},
 	{
-		.freq_low	= 1482000,
-		.freq_high	= 1595999,
+		.freq_low	= 1432000,
+		.freq_high	= 1575999,
 		.VCOdivider	= 2,
-		.progdata	= (0 << 18) | (1 << 9) | 0x08,
+		.progdata	= (0 << 19) | (1 << 9) | 0x04,
 	},
 	{
-		.freq_low	= 1596000,
+		.freq_low	= 1576000,
 		.freq_high	= 1717999,
 		.VCOdivider	= 2,
-		.progdata	= (0 << 18) | (1 << 9) | 0x10,
+		.progdata	= (0 << 19) | (1 << 9) | 0x08,
 	},
 	{
 		.freq_low	= 1718000,
 		.freq_high	= 1855999,
 		.VCOdivider	= 2,
-		.progdata	= (0 << 18) | (1 << 9) | 0x20,
+		.progdata	= (0 << 19) | (1 << 9) | 0x10,
 	},
 	{
 		.freq_low	= 1856000,
 		.freq_high	= 2035999,
 		.VCOdivider	= 2,
-		.progdata	= (0 << 18) | (1 << 9) | 0x40,
+		.progdata	= (0 << 19) | (1 << 9) | 0x20,
 	},
 	{
 		.freq_low	= 2036000,
-		.freq_high	= 2149999,
+		.freq_high	= 2150000,
 		.VCOdivider	= 2,
-		.progdata	= (0 << 18) | (1 << 9) | 0x80,
+		.progdata	= (0 << 19) | (1 << 9) | 0x40,
 	},
+#if 0
+/* This band is not useful with the /2 divider, as its center frequency
+   is approximately 2300MHz, which is outside of the tunable range. It is
+   useful only with the /4 divider, as used in band #2. */
+	{
+		.freq_low	= 2150000,
+		.freq_high	= 2356000,
+		.VCOdivider	= 2,
+		.progdata	= (0 << 19) | (1 << 9) | 0x80,
+	},
+#endif
 };
 
 static struct {
@@ -178,45 +180,44 @@
 {
 	{0x00, 0x03}, /* Reset system */
 	{0x00, 0x00}, /* Clear reset */
-	{0x03, 0x07},
-	{0x04, 0x10},
-	{0x05, 0x04},
-	{0x06, 0x31},
-	{0x0d, 0x02},
-	{0x0e, 0x03},
-	{0x0f, 0xfe},
-	{0x10, 0x01},
-	{0x14, 0x01},
-	{0x16, 0x00},
-	{0x17, 0x01},
-	{0x1b, 0x05},
-	{0x1c, 0x80},
-	{0x1d, 0x00},
-	{0x1e, 0x00},
-	{0x20, 0x41},
-	{0x21, 0x15},
-	{0x29, 0x00},
-	{0x2a, 0xb0},
-	{0x2b, 0x73},
-	{0x2c, 0x00},
+	{0x03, 0x07}, /* QPSK, DVB, Auto Acquisition (default) */
+	{0x04, 0x10}, /* MPEG */
+	{0x05, 0x04}, /* MPEG */
+	{0x06, 0x31}, /* MPEG (default) */
+	{0x0b, 0x00}, /* Freq search start point (default) */
+	{0x0c, 0x00}, /* Demodulator sample gain (default) */
+	{0x0d, 0x02}, /* Frequency search range = Fsymbol / 4 (default) */
+	{0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */
+	{0x0f, 0xfe}, /* FEC search mask (all supported codes) */
+	{0x10, 0x01}, /* Default search inversion, no repeat (default) */
+	{0x16, 0x00}, /* Enable reading of frequency */
+	{0x17, 0x01}, /* Enable EsNO Ready Counter */
+	{0x1c, 0x80}, /* Enable error counter */
+	{0x20, 0x00}, /* Tuner burst clock rate = 500KHz */
+	{0x21, 0x15}, /* Tuner burst mode, word length = 0x15 */
+	{0x28, 0x00}, /* Enable FILTERV with positive pol., DiSEqC 2.x off */
+	{0x29, 0x00}, /* DiSEqC LNB_DC off */
+	{0x2a, 0xb0}, /* DiSEqC Parameters (default) */
+	{0x2b, 0x73}, /* DiSEqC Tone Frequency (default) */
+	{0x2c, 0x00}, /* DiSEqC Message (0x2c - 0x31) */ 
 	{0x2d, 0x00},
 	{0x2e, 0x00},
 	{0x2f, 0x00},
 	{0x30, 0x00},
 	{0x31, 0x00},
-	{0x32, 0x8c},
-	{0x33, 0x00},
+	{0x32, 0x8c}, /* DiSEqC Parameters (default) */
+	{0x33, 0x00}, /* Interrupts off (0x33 - 0x34) */
 	{0x34, 0x00},
-	{0x35, 0x03},
-	{0x36, 0x02},
-	{0x37, 0x3a},
-	{0x3a, 0x00},	/* Enable AGC accumulator */
-	{0x44, 0x00},
-	{0x45, 0x00},
-	{0x46, 0x05},
-	{0x56, 0x41},
-	{0x57, 0xff},
-	{0x67, 0x83},
+	{0x35, 0x03}, /* DiSEqC Tone Amplitude (default) */ 
+	{0x36, 0x02}, /* DiSEqC Parameters (default) */
+	{0x37, 0x3a}, /* DiSEqC Parameters (default) */
+	{0x3a, 0x00}, /* Enable AGC accumulator (for signal strength) */
+	{0x44, 0x00}, /* Constellation (default) */
+	{0x45, 0x00}, /* Symbol count (default) */
+	{0x46, 0x0d}, /* Symbol rate estimator on (default) */
+	{0x56, 0x41}, /* Various (default) */
+	{0x57, 0xff}, /* Error Counter Window (default) */
+	{0x67, 0x83}, /* Non-DCII symbol clock */ 
 };
 
 static int cx24123_writereg(struct cx24123_state* state, int reg, int data)
@@ -280,17 +281,20 @@
 
 static int cx24123_set_inversion(struct cx24123_state* state, fe_spectral_inversion_t inversion)
 {
+	u8 nom_reg = cx24123_readreg(state, 0x0e);
+	u8 auto_reg = cx24123_readreg(state, 0x10);
+
 	switch (inversion) {
 	case INVERSION_OFF:
-		cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) & 0x7f);
-		cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80);
+		cx24123_writereg(state, 0x0e, nom_reg & ~0x80);
+		cx24123_writereg(state, 0x10, auto_reg | 0x80);
 		break;
 	case INVERSION_ON:
-		cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) | 0x80);
-		cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x80);
+		cx24123_writereg(state, 0x10, auto_reg | 0x80);
 		break;
 	case INVERSION_AUTO:
-		cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) & 0x7f);
+		cx24123_writereg(state, 0x10, auto_reg & ~0x80);
 		break;
 	default:
 		return -EINVAL;
@@ -315,28 +319,48 @@
 
 static int cx24123_set_fec(struct cx24123_state* state, fe_code_rate_t fec)
 {
-	if ( (fec < FEC_NONE) || (fec > FEC_AUTO) )
+	u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07;
+
+	if ( (fec <= FEC_NONE) || (fec > FEC_AUTO) )
 		fec = FEC_AUTO;
 
-	/* Hardware has 5/11 and 3/5 but are never unused */
 	switch (fec) {
-	case FEC_NONE:
-		return cx24123_writereg(state, 0x0f, 0x01);
 	case FEC_1_2:
-		return cx24123_writereg(state, 0x0f, 0x02);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x01);
+		cx24123_writereg(state, 0x0f, 0x02);
+		break;
 	case FEC_2_3:
-		return cx24123_writereg(state, 0x0f, 0x04);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x02);
+		cx24123_writereg(state, 0x0f, 0x04);
+		break;
 	case FEC_3_4:
-		return cx24123_writereg(state, 0x0f, 0x08);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x03);
+		cx24123_writereg(state, 0x0f, 0x08);
+		break;
+	case FEC_4_5:
+		cx24123_writereg(state, 0x0e, nom_reg | 0x04);
+		cx24123_writereg(state, 0x0f, 0x10);
+		break;
 	case FEC_5_6:
-		return cx24123_writereg(state, 0x0f, 0x20);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x05);
+		cx24123_writereg(state, 0x0f, 0x20);
+		break;
+	case FEC_6_7:
+		cx24123_writereg(state, 0x0e, nom_reg | 0x06);
+		cx24123_writereg(state, 0x0f, 0x40);
+		break;
 	case FEC_7_8:
-		return cx24123_writereg(state, 0x0f, 0x80);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x07);
+		cx24123_writereg(state, 0x0f, 0x80);
+		break;
 	case FEC_AUTO:
-		return cx24123_writereg(state, 0x0f, 0xae);
+		cx24123_writereg(state, 0x0f, 0xfe);
+		break;
 	default:
 		return -EOPNOTSUPP;
 	}
+
+	return 0;
 }
 
 static int cx24123_get_fec(struct cx24123_state* state, fe_code_rate_t *fec)
@@ -371,16 +395,31 @@
 		*fec = FEC_7_8;
 		break;
 	default:
-		*fec = FEC_NONE; // can't happen
-		printk("FEC_NONE ?\n");
+		/* this can happen when there's no lock */
+		*fec = FEC_NONE;
 	}
 
 	return 0;
 }
 
+/* Approximation of closest integer of log2(a/b). It actually gives the
+   lowest integer i such that 2^i >= round(a/b) */
+static u32 cx24123_int_log2(u32 a, u32 b)
+{
+	u32 exp, nearest = 0;
+	u32 div = a / b;
+	if(a % b >= b / 2) ++div; 
+	if(div < (1 << 31))
+	{
+		for(exp = 1; div > exp; nearest++)
+			exp += exp;
+	}
+	return nearest;
+}
+
 static int cx24123_set_symbolrate(struct cx24123_state* state, u32 srate)
 {
-	u32 tmp, sample_rate, ratio;
+	u32 tmp, sample_rate, ratio, sample_gain;
 	u8 pll_mult;
 
 	/*  check if symbol rate is within limits */
@@ -438,7 +477,12 @@
 	cx24123_writereg(state, 0x09, (ratio >>  8) & 0xff );
 	cx24123_writereg(state, 0x0a, (ratio      ) & 0xff );
 
-	dprintk("%s: srate=%d, ratio=0x%08x, sample_rate=%i\n", __FUNCTION__, srate, ratio, sample_rate);
+	/* also set the demodulator sample gain */
+	sample_gain = cx24123_int_log2(sample_rate, srate);
+	tmp = cx24123_readreg(state, 0x0c) & ~0xe0;
+	cx24123_writereg(state, 0x0c, tmp | sample_gain << 5);
+
+	dprintk("%s: srate=%d, ratio=0x%08x, sample_rate=%i sample_gain=%d\n", __FUNCTION__, srate, ratio, sample_rate, sample_gain);
 
 	return 0;
 }
@@ -453,6 +497,7 @@
 	u32 ndiv = 0, adiv = 0, vco_div = 0;
 	int i = 0;
 	int pump = 2;
+	int band = 0;
 
 	/* Defaults for low freq, low rate */
 	state->VCAarg = cx24123_AGC_vals[0].VCAprogdata;
@@ -471,21 +516,27 @@
 		}
 	}
 
-	/* For the given frequency, determine the bandselect programming bits */
-	for (i = 0; i < sizeof(cx24123_bandselect_vals) / sizeof(cx24123_bandselect_vals[0]); i++)
+	/* determine the band to use */
+	if(force_band == 0)
 	{
-		if ((cx24123_bandselect_vals[i].freq_low <= p->frequency) &&
-		    (cx24123_bandselect_vals[i].freq_high >= p->frequency) ) {
-			state->bandselectarg = cx24123_bandselect_vals[i].progdata;
-			vco_div = cx24123_bandselect_vals[i].VCOdivider;
-
-			/* determine the charge pump current */
-			if ( p->frequency < (cx24123_bandselect_vals[i].freq_low + cx24123_bandselect_vals[i].freq_high)/2 )
-				pump = 0x01;
-			else
-				pump = 0x02;
+		for (i = 0; i < sizeof(cx24123_bandselect_vals) / sizeof(cx24123_bandselect_vals[0]); i++)
+		{
+			if ((cx24123_bandselect_vals[i].freq_low <= p->frequency) &&
+			    (cx24123_bandselect_vals[i].freq_high >= p->frequency) )
+				band = i;
 		}
 	}
+	else
+		band = force_band;
+
+	state->bandselectarg = cx24123_bandselect_vals[band].progdata;
+	vco_div = cx24123_bandselect_vals[band].VCOdivider;
+
+	/* determine the charge pump current */
+	if ( p->frequency < (cx24123_bandselect_vals[band].freq_low + cx24123_bandselect_vals[band].freq_high)/2 )
+		pump = 0x01;
+	else
+		pump = 0x02;
 
 	/* Determine the N/A dividers for the requested lband freq (in kHz). */
 	/* Note: the reference divider R=10, frequency is in KHz, XTAL is in Hz */
@@ -752,35 +803,44 @@
 	return 0;
 }
 
-/*
- * Configured to return the measurement of errors in blocks, because no UCBLOCKS value
- * is available, so this value doubles up to satisfy both measurements
- */
-static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber)
+static void cx24123_read_error_rate(struct dvb_frontend* fe, u32* ber, u16* snr)
 {
 	struct cx24123_state *state = fe->demodulator_priv;
 
-	state->lastber =
+	u32 cur_ber = 
 		((cx24123_readreg(state, 0x1c) & 0x3f) << 16) |
 		(cx24123_readreg(state, 0x1d) << 8 |
 		cx24123_readreg(state, 0x1e));
 
+	if(ber)
+		 *ber = cur_ber;
+
 	/* Do the signal quality processing here, it's derived from the BER. */
 	/* Scale the BER from a 24bit to a SNR 16 bit where higher = better */
-	if (state->lastber < 5000)
-		state->snr = 655*100;
-	else if ( (state->lastber >=   5000) && (state->lastber <  55000) )
-		state->snr = 655*90;
-	else if ( (state->lastber >=  55000) && (state->lastber < 150000) )
-		state->snr = 655*80;
-	else if ( (state->lastber >= 150000) && (state->lastber < 250000) )
-		state->snr = 655*70;
-	else if ( (state->lastber >= 250000) && (state->lastber < 450000) )
-		state->snr = 655*65;
-	else
-		state->snr = 0;
+	if(snr)
+	{
+		if (cur_ber < 5000)
+			*snr = 655*100;
+		else if ( (cur_ber >=   5000) && (cur_ber <  55000) )
+			*snr = 655*90;
+		else if ( (cur_ber >=  55000) && (cur_ber < 150000) )
+			*snr = 655*80;
+		else if ( (cur_ber >= 150000) && (cur_ber < 250000) )
+			*snr = 655*70;
+		else if ( (cur_ber >= 250000) && (cur_ber < 450000) )
+			*snr = 655*65;
+		else
+			*snr = 0;
+	}
+}
 
-	*ber = state->lastber;
+/*
+ * Configured to return the measurement of errors in blocks, because no UCBLOCKS value
+ * is available, so this value doubles up to satisfy both measurements
+ */
+static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	cx24123_read_error_rate(fe, ber, NULL);
 
 	return 0;
 }
@@ -795,16 +855,14 @@
 
 static int cx24123_read_snr(struct dvb_frontend* fe, u16* snr)
 {
-	struct cx24123_state *state = fe->demodulator_priv;
-	*snr = state->snr;
+	cx24123_read_error_rate(fe, NULL, snr);
 
 	return 0;
 }
 
 static int cx24123_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
 {
-	struct cx24123_state *state = fe->demodulator_priv;
-	*ucblocks = state->lastber;
+	cx24123_read_error_rate(fe, ucblocks, NULL);
 
 	return 0;
 }
@@ -918,8 +976,6 @@
 	state->config = config;
 	state->i2c = i2c;
 	memcpy(&state->ops, &cx24123_ops, sizeof(struct dvb_frontend_ops));
-	state->lastber = 0;
-	state->snr = 0;
 	state->lnbreg = 0;
 	state->VCAarg = 0;
 	state->VGAarg = 0;
@@ -954,12 +1010,13 @@
 		.frequency_min = 950000,
 		.frequency_max = 2150000,
 		.frequency_stepsize = 1011, /* kHz for QPSK frontends */
-		.frequency_tolerance = 29500,
+		.frequency_tolerance = 5000,
 		.symbol_rate_min = 1000000,
 		.symbol_rate_max = 45000000,
 		.caps = FE_CAN_INVERSION_AUTO |
 			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
 			FE_CAN_QPSK | FE_CAN_RECOVER
 	},
 
@@ -982,6 +1039,9 @@
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
+module_param(force_band, int, 0644);
+MODULE_PARM_DESC(force_band, "Force a specific band select (1-10, default:off).");
+
 MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24123/cx24109 hardware");
 MODULE_AUTHOR("Steven Toth");
 MODULE_LICENSE("GPL");
--- v4l-dvb/linux/drivers/media/dvb/frontends/cx24123.c.orig	2006-03-20 23:22:07.000000000 -0500
+++ v4l-dvb/linux/drivers/media/dvb/frontends/cx24123.c	2006-03-24 13:13:27.000000000 -0500
@@ -29,6 +29,9 @@
 #include "dvb_frontend.h"
 #include "cx24123.h"
 
+#define XTAL 10111000
+
+static int force_band;
 static int debug;
 #define dprintk(args...) \
 	do { \
@@ -43,8 +46,6 @@
 
 	struct dvb_frontend frontend;
 
-	u32 lastber;
-	u16 snr;
 	u8  lnbreg;
 
 	/* Some PLL specifics for tuning */
@@ -52,6 +53,7 @@
 	u32 VGAarg;
 	u32 bandselectarg;
 	u32 pllarg;
+	u32 FILTune;
 
 	/* The Demod/Tuner can't easily provide these, we cache them */
 	u32 currentfreq;
@@ -63,43 +65,31 @@
 {
 	u32 symbolrate_low;
 	u32 symbolrate_high;
-	u32 VCAslope;
-	u32 VCAoffset;
-	u32 VGA1offset;
-	u32 VGA2offset;
 	u32 VCAprogdata;
 	u32 VGAprogdata;
+	u32 FILTune;
 } cx24123_AGC_vals[] =
 {
 	{
 		.symbolrate_low		= 1000000,
 		.symbolrate_high	= 4999999,
-		.VCAslope		= 0x07,
-		.VCAoffset		= 0x0f,
-		.VGA1offset		= 0x1f8,
-		.VGA2offset		= 0x1f8,
-		.VGAprogdata		= (2 << 18) | (0x1f8 << 9) | 0x1f8,
-		.VCAprogdata		= (4 << 18) | (0x07 << 9) | 0x07,
+		.VGAprogdata		= (1 << 19) | (0x1f8 << 9) | 0x1f8,
+		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x07,
+		.FILTune		= 0x27f /* 0.41 V */
 	},
 	{
 		.symbolrate_low		=  5000000,
 		.symbolrate_high	= 14999999,
-		.VCAslope		= 0x1f,
-		.VCAoffset		= 0x1f,
-		.VGA1offset		= 0x1e0,
-		.VGA2offset		= 0x180,
-		.VGAprogdata		= (2 << 18) | (0x180 << 9) | 0x1e0,
-		.VCAprogdata		= (4 << 18) | (0x07 << 9) | 0x1f,
+		.VGAprogdata		= (1 << 19) | (0x180 << 9) | 0x1e0,
+		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x1f,
+		.FILTune		= 0x317 /* 0.90 V */
 	},
 	{
 		.symbolrate_low		= 15000000,
 		.symbolrate_high	= 45000000,
-		.VCAslope		= 0x3f,
-		.VCAoffset		= 0x3f,
-		.VGA1offset		= 0x180,
-		.VGA2offset		= 0x100,
-		.VGAprogdata		= (2 << 18) | (0x100 << 9) | 0x180,
-		.VCAprogdata		= (4 << 18) | (0x07 << 9) | 0x3f,
+		.VGAprogdata		= (1 << 19) | (0x100 << 9) | 0x180,
+		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x3f,
+		.FILTune		= 0x145 /* 2.70 V */
 	},
 };
 
@@ -112,92 +102,75 @@
 {
 	u32 freq_low;
 	u32 freq_high;
-	u32 bandselect;
 	u32 VCOdivider;
-	u32 VCOnumber;
 	u32 progdata;
 } cx24123_bandselect_vals[] =
 {
 	{
 		.freq_low	= 950000,
-		.freq_high	= 1018999,
-		.bandselect	= 0x40,
-		.VCOdivider	= 4,
-		.VCOnumber	= 7,
-		.progdata	= (0 << 18) | (0 << 9) | 0x40,
-	},
-	{
-		.freq_low	= 1019000,
 		.freq_high	= 1074999,
-		.bandselect	= 0x80,
 		.VCOdivider	= 4,
-		.VCOnumber	= 8,
-		.progdata	= (0 << 18) | (0 << 9) | 0x80,
+		.progdata	= (0 << 19) | (0 << 9) | 0x40,
 	},
 	{
 		.freq_low	= 1075000,
-		.freq_high	= 1227999,
-		.bandselect	= 0x01,
-		.VCOdivider	= 2,
-		.VCOnumber	= 1,
-		.progdata	= (0 << 18) | (1 << 9) | 0x01,
+		.freq_high	= 1177999,
+		.VCOdivider	= 4,
+		.progdata	= (0 << 19) | (0 << 9) | 0x80,
 	},
 	{
-		.freq_low	= 1228000,
-		.freq_high	= 1349999,
-		.bandselect	= 0x02,
+		.freq_low	= 1178000,
+		.freq_high	= 1295999,
 		.VCOdivider	= 2,
-		.VCOnumber	= 2,
-		.progdata	= (0 << 18) | (1 << 9) | 0x02,
+		.progdata	= (0 << 19) | (1 << 9) | 0x01,
 	},
 	{
-		.freq_low	= 1350000,
-		.freq_high	= 1481999,
-		.bandselect	= 0x04,
+		.freq_low	= 1296000,
+		.freq_high	= 1431999,
 		.VCOdivider	= 2,
-		.VCOnumber	= 3,
-		.progdata	= (0 << 18) | (1 << 9) | 0x04,
+		.progdata	= (0 << 19) | (1 << 9) | 0x02,
 	},
 	{
-		.freq_low	= 1482000,
-		.freq_high	= 1595999,
-		.bandselect	= 0x08,
+		.freq_low	= 1432000,
+		.freq_high	= 1575999,
 		.VCOdivider	= 2,
-		.VCOnumber	= 4,
-		.progdata	= (0 << 18) | (1 << 9) | 0x08,
+		.progdata	= (0 << 19) | (1 << 9) | 0x04,
 	},
 	{
-		.freq_low	= 1596000,
+		.freq_low	= 1576000,
 		.freq_high	= 1717999,
-		.bandselect	= 0x10,
 		.VCOdivider	= 2,
-		.VCOnumber	= 5,
-		.progdata	= (0 << 18) | (1 << 9) | 0x10,
+		.progdata	= (0 << 19) | (1 << 9) | 0x08,
 	},
 	{
 		.freq_low	= 1718000,
 		.freq_high	= 1855999,
-		.bandselect	= 0x20,
 		.VCOdivider	= 2,
-		.VCOnumber	= 6,
-		.progdata	= (0 << 18) | (1 << 9) | 0x20,
+		.progdata	= (0 << 19) | (1 << 9) | 0x10,
 	},
 	{
 		.freq_low	= 1856000,
 		.freq_high	= 2035999,
-		.bandselect	= 0x40,
 		.VCOdivider	= 2,
-		.VCOnumber	= 7,
-		.progdata	= (0 << 18) | (1 << 9) | 0x40,
+		.progdata	= (0 << 19) | (1 << 9) | 0x20,
 	},
 	{
 		.freq_low	= 2036000,
-		.freq_high	= 2149999,
-		.bandselect	= 0x80,
+		.freq_high	= 2150000,
 		.VCOdivider	= 2,
-		.VCOnumber	= 8,
-		.progdata	= (0 << 18) | (1 << 9) | 0x80,
+		.progdata	= (0 << 19) | (1 << 9) | 0x40,
 	},
+#if 0
+/* This band is not useful with the /2 divider, as its center frequency
+   is approximately 2300MHz, which is outside of the tunable range. It is
+   useful only with the /4 divider, as used in band #2. */
+	{
+		.freq_low	= 2150000,
+		.freq_high	= 2356000,
+		.VCOdivider	= 2,
+		.progdata	= (0 << 19) | (1 << 9) | 0x80,
+	},
+#endif
 };
 
 static struct {
@@ -207,49 +180,44 @@
 {
 	{0x00, 0x03}, /* Reset system */
 	{0x00, 0x00}, /* Clear reset */
-	{0x01, 0x3b}, /* Apply sensible defaults, from an i2c sniffer */
-	{0x03, 0x07},
-	{0x04, 0x10},
-	{0x05, 0x04},
-	{0x06, 0x31},
-	{0x0d, 0x02},
-	{0x0e, 0x03},
-	{0x0f, 0xfe},
-	{0x10, 0x01},
-	{0x14, 0x01},
-	{0x15, 0x98},
-	{0x16, 0x00},
-	{0x17, 0x01},
-	{0x1b, 0x05},
-	{0x1c, 0x80},
-	{0x1d, 0x00},
-	{0x1e, 0x00},
-	{0x20, 0x41},
-	{0x21, 0x15},
-	{0x27, 0x14},
-	{0x28, 0x46},
-	{0x29, 0x00},
-	{0x2a, 0xb0},
-	{0x2b, 0x73},
-	{0x2c, 0x00},
+	{0x03, 0x07}, /* QPSK, DVB, Auto Acquisition (default) */
+	{0x04, 0x10}, /* MPEG */
+	{0x05, 0x04}, /* MPEG */
+	{0x06, 0x31}, /* MPEG (default) */
+	{0x0b, 0x00}, /* Freq search start point (default) */
+	{0x0c, 0x00}, /* Demodulator sample gain (default) */
+	{0x0d, 0x02}, /* Frequency search range = Fsymbol / 4 (default) */
+	{0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */
+	{0x0f, 0xfe}, /* FEC search mask (all supported codes) */
+	{0x10, 0x01}, /* Default search inversion, no repeat (default) */
+	{0x16, 0x00}, /* Enable reading of frequency */
+	{0x17, 0x01}, /* Enable EsNO Ready Counter */
+	{0x1c, 0x80}, /* Enable error counter */
+	{0x20, 0x00}, /* Tuner burst clock rate = 500KHz */
+	{0x21, 0x15}, /* Tuner burst mode, word length = 0x15 */
+	{0x28, 0x00}, /* Enable FILTERV with positive pol., DiSEqC 2.x off */
+	{0x29, 0x00}, /* DiSEqC LNB_DC off */
+	{0x2a, 0xb0}, /* DiSEqC Parameters (default) */
+	{0x2b, 0x73}, /* DiSEqC Tone Frequency (default) */
+	{0x2c, 0x00}, /* DiSEqC Message (0x2c - 0x31) */ 
 	{0x2d, 0x00},
 	{0x2e, 0x00},
 	{0x2f, 0x00},
 	{0x30, 0x00},
 	{0x31, 0x00},
-	{0x32, 0x8c},
-	{0x33, 0x00},
+	{0x32, 0x8c}, /* DiSEqC Parameters (default) */
+	{0x33, 0x00}, /* Interrupts off (0x33 - 0x34) */
 	{0x34, 0x00},
-	{0x35, 0x03},
-	{0x36, 0x02},
-	{0x37, 0x3a},
-	{0x3a, 0x00},	/* Enable AGC accumulator */
-	{0x44, 0x00},
-	{0x45, 0x00},
-	{0x46, 0x05},
-	{0x56, 0x41},
-	{0x57, 0xff},
-	{0x67, 0x83},
+	{0x35, 0x03}, /* DiSEqC Tone Amplitude (default) */ 
+	{0x36, 0x02}, /* DiSEqC Parameters (default) */
+	{0x37, 0x3a}, /* DiSEqC Parameters (default) */
+	{0x3a, 0x00}, /* Enable AGC accumulator (for signal strength) */
+	{0x44, 0x00}, /* Constellation (default) */
+	{0x45, 0x00}, /* Symbol count (default) */
+	{0x46, 0x0d}, /* Symbol rate estimator on (default) */
+	{0x56, 0x41}, /* Various (default) */
+	{0x57, 0xff}, /* Error Counter Window (default) */
+	{0x67, 0x83}, /* Non-DCII symbol clock */ 
 };
 
 static int cx24123_writereg(struct cx24123_state* state, int reg, int data)
@@ -313,17 +281,20 @@
 
 static int cx24123_set_inversion(struct cx24123_state* state, fe_spectral_inversion_t inversion)
 {
+	u8 nom_reg = cx24123_readreg(state, 0x0e);
+	u8 auto_reg = cx24123_readreg(state, 0x10);
+
 	switch (inversion) {
 	case INVERSION_OFF:
-		cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) & 0x7f);
-		cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80);
+		cx24123_writereg(state, 0x0e, nom_reg & ~0x80);
+		cx24123_writereg(state, 0x10, auto_reg | 0x80);
 		break;
 	case INVERSION_ON:
-		cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) | 0x80);
-		cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x80);
+		cx24123_writereg(state, 0x10, auto_reg | 0x80);
 		break;
 	case INVERSION_AUTO:
-		cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) & 0x7f);
+		cx24123_writereg(state, 0x10, auto_reg & ~0x80);
 		break;
 	default:
 		return -EINVAL;
@@ -348,82 +319,170 @@
 
 static int cx24123_set_fec(struct cx24123_state* state, fe_code_rate_t fec)
 {
-	if ( (fec < FEC_NONE) || (fec > FEC_AUTO) )
+	u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07;
+
+	if ( (fec <= FEC_NONE) || (fec > FEC_AUTO) )
 		fec = FEC_AUTO;
 
-	/* Hardware has 5/11 and 3/5 but are never unused */
 	switch (fec) {
-	case FEC_NONE:
-		return cx24123_writereg(state, 0x0f, 0x01);
 	case FEC_1_2:
-		return cx24123_writereg(state, 0x0f, 0x02);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x01);
+		cx24123_writereg(state, 0x0f, 0x02);
+		break;
 	case FEC_2_3:
-		return cx24123_writereg(state, 0x0f, 0x04);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x02);
+		cx24123_writereg(state, 0x0f, 0x04);
+		break;
 	case FEC_3_4:
-		return cx24123_writereg(state, 0x0f, 0x08);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x03);
+		cx24123_writereg(state, 0x0f, 0x08);
+		break;
+	case FEC_4_5:
+		cx24123_writereg(state, 0x0e, nom_reg | 0x04);
+		cx24123_writereg(state, 0x0f, 0x10);
+		break;
 	case FEC_5_6:
-		return cx24123_writereg(state, 0x0f, 0x20);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x05);
+		cx24123_writereg(state, 0x0f, 0x20);
+		break;
+	case FEC_6_7:
+		cx24123_writereg(state, 0x0e, nom_reg | 0x06);
+		cx24123_writereg(state, 0x0f, 0x40);
+		break;
 	case FEC_7_8:
-		return cx24123_writereg(state, 0x0f, 0x80);
+		cx24123_writereg(state, 0x0e, nom_reg | 0x07);
+		cx24123_writereg(state, 0x0f, 0x80);
+		break;
 	case FEC_AUTO:
-		return cx24123_writereg(state, 0x0f, 0xae);
+		cx24123_writereg(state, 0x0f, 0xfe);
+		break;
 	default:
 		return -EOPNOTSUPP;
 	}
+
+	return 0;
 }
 
 static int cx24123_get_fec(struct cx24123_state* state, fe_code_rate_t *fec)
 {
 	int ret;
-	u8 val;
 
 	ret = cx24123_readreg (state, 0x1b);
 	if (ret < 0)
 		return ret;
-	val = ret & 0x07;
-	switch (val) {
+	ret = ret & 0x07;
+
+	switch (ret) {
 	case 1:
 		*fec = FEC_1_2;
 		break;
-	case 3:
+	case 2:
 		*fec = FEC_2_3;
 		break;
-	case 4:
+	case 3:
 		*fec = FEC_3_4;
 		break;
-	case 5:
+	case 4:
 		*fec = FEC_4_5;
 		break;
-	case 6:
+	case 5:
 		*fec = FEC_5_6;
 		break;
+	case 6:
+		*fec = FEC_6_7;
+		break;
 	case 7:
 		*fec = FEC_7_8;
 		break;
-	case 2:	/* *fec = FEC_3_5; break; */
-	case 0:	/* *fec = FEC_5_11; break; */
-		*fec = FEC_AUTO;
-		break;
 	default:
-		*fec = FEC_NONE; // can't happen
+		/* this can happen when there's no lock */
+		*fec = FEC_NONE;
 	}
 
 	return 0;
 }
 
-/* fixme: Symbol rates < 3MSps may not work because of precision loss */
+/* Approximation of closest integer of log2(a/b). It actually gives the
+   lowest integer i such that 2^i >= round(a/b) */
+static u32 cx24123_int_log2(u32 a, u32 b)
+{
+	u32 exp, nearest = 0;
+	u32 div = a / b;
+	if(a % b >= b / 2) ++div; 
+	if(div < (1 << 31))
+	{
+		for(exp = 1; div > exp; nearest++)
+			exp += exp;
+	}
+	return nearest;
+}
+
 static int cx24123_set_symbolrate(struct cx24123_state* state, u32 srate)
 {
-	u32 val;
+	u32 tmp, sample_rate, ratio, sample_gain;
+	u8 pll_mult;
+
+	/*  check if symbol rate is within limits */
+	if ((srate > state->ops.info.symbol_rate_max) ||
+	    (srate < state->ops.info.symbol_rate_min))
+		return -EOPNOTSUPP;;
+
+	/* choose the sampling rate high enough for the required operation,
+	   while optimizing the power consumed by the demodulator */
+	if (srate < (XTAL*2)/2)
+		pll_mult = 2;
+	else if (srate < (XTAL*3)/2)
+		pll_mult = 3;
+	else if (srate < (XTAL*4)/2)
+		pll_mult = 4;
+	else if (srate < (XTAL*5)/2)
+		pll_mult = 5;
+	else if (srate < (XTAL*6)/2)
+		pll_mult = 6;
+	else if (srate < (XTAL*7)/2)
+		pll_mult = 7;
+	else if (srate < (XTAL*8)/2)
+		pll_mult = 8;
+	else
+		pll_mult = 9;
+
+
+	sample_rate = pll_mult * XTAL;
+
+	/*
+	    SYSSymbolRate[21:0] = (srate << 23) / sample_rate
 
-	val = (srate / 1185) * 100;
+	    We have to use 32 bit unsigned arithmetic without precision loss.
+	    The maximum srate is 45000000 or 0x02AEA540. This number has
+	    only 6 clear bits on top, hence we can shift it left only 6 bits
+	    at a time. Borrowed from cx24110.c
+	*/
 
-	/* Compensate for scaling up, by removing 17 symbols per 1Msps */
-	val = val - (17 * (srate / 1000000));
+	tmp = srate << 6;
+	ratio = tmp / sample_rate;
 
-	cx24123_writereg(state, 0x08, (val >> 16) & 0xff );
-	cx24123_writereg(state, 0x09, (val >>  8) & 0xff );
-	cx24123_writereg(state, 0x0a, (val      ) & 0xff );
+	tmp = (tmp % sample_rate) << 6;
+	ratio = (ratio << 6) + (tmp / sample_rate);
+
+	tmp = (tmp % sample_rate) << 6;
+	ratio = (ratio << 6) + (tmp / sample_rate);
+
+	tmp = (tmp % sample_rate) << 5;
+	ratio = (ratio << 5) + (tmp / sample_rate);
+
+
+	cx24123_writereg(state, 0x01, pll_mult * 6);
+
+	cx24123_writereg(state, 0x08, (ratio >> 16) & 0x3f );
+	cx24123_writereg(state, 0x09, (ratio >>  8) & 0xff );
+	cx24123_writereg(state, 0x0a, (ratio      ) & 0xff );
+
+	/* also set the demodulator sample gain */
+	sample_gain = cx24123_int_log2(sample_rate, srate);
+	tmp = cx24123_readreg(state, 0x0c) & ~0xe0;
+	cx24123_writereg(state, 0x0c, tmp | sample_gain << 5);
+
+	dprintk("%s: srate=%d, ratio=0x%08x, sample_rate=%i sample_gain=%d\n", __FUNCTION__, srate, ratio, sample_rate, sample_gain);
 
 	return 0;
 }
@@ -437,6 +496,8 @@
 	struct cx24123_state *state = fe->demodulator_priv;
 	u32 ndiv = 0, adiv = 0, vco_div = 0;
 	int i = 0;
+	int pump = 2;
+	int band = 0;
 
 	/* Defaults for low freq, low rate */
 	state->VCAarg = cx24123_AGC_vals[0].VCAprogdata;
@@ -444,38 +505,49 @@
 	state->bandselectarg = cx24123_bandselect_vals[0].progdata;
 	vco_div = cx24123_bandselect_vals[0].VCOdivider;
 
-	/* For the given symbolerate, determine the VCA and VGA programming bits */
+	/* For the given symbol rate, determine the VCA, VGA and FILTUNE programming bits */
 	for (i = 0; i < sizeof(cx24123_AGC_vals) / sizeof(cx24123_AGC_vals[0]); i++)
 	{
 		if ((cx24123_AGC_vals[i].symbolrate_low <= p->u.qpsk.symbol_rate) &&
-				(cx24123_AGC_vals[i].symbolrate_high >= p->u.qpsk.symbol_rate) ) {
+		    (cx24123_AGC_vals[i].symbolrate_high >= p->u.qpsk.symbol_rate) ) {
 			state->VCAarg = cx24123_AGC_vals[i].VCAprogdata;
 			state->VGAarg = cx24123_AGC_vals[i].VGAprogdata;
+			state->FILTune = cx24123_AGC_vals[i].FILTune;
 		}
 	}
 
-	/* For the given frequency, determine the bandselect programming bits */
-	for (i = 0; i < sizeof(cx24123_bandselect_vals) / sizeof(cx24123_bandselect_vals[0]); i++)
+	/* determine the band to use */
+	if(force_band == 0)
 	{
-		if ((cx24123_bandselect_vals[i].freq_low <= p->frequency) &&
-				(cx24123_bandselect_vals[i].freq_high >= p->frequency) ) {
-			state->bandselectarg = cx24123_bandselect_vals[i].progdata;
-			vco_div = cx24123_bandselect_vals[i].VCOdivider;
+		for (i = 0; i < sizeof(cx24123_bandselect_vals) / sizeof(cx24123_bandselect_vals[0]); i++)
+		{
+			if ((cx24123_bandselect_vals[i].freq_low <= p->frequency) &&
+			    (cx24123_bandselect_vals[i].freq_high >= p->frequency) )
+				band = i;
 		}
 	}
+	else
+		band = force_band;
+
+	state->bandselectarg = cx24123_bandselect_vals[band].progdata;
+	vco_div = cx24123_bandselect_vals[band].VCOdivider;
+
+	/* determine the charge pump current */
+	if ( p->frequency < (cx24123_bandselect_vals[band].freq_low + cx24123_bandselect_vals[band].freq_high)/2 )
+		pump = 0x01;
+	else
+		pump = 0x02;
 
 	/* Determine the N/A dividers for the requested lband freq (in kHz). */
-	/* Note: 10111 (kHz) is the Crystal Freq and divider of 10. */
-	ndiv = ( ((p->frequency * vco_div) / (10111 / 10) / 2) / 32) & 0x1ff;
-	adiv = ( ((p->frequency * vco_div) / (10111 / 10) / 2) % 32) & 0x1f;
+	/* Note: the reference divider R=10, frequency is in KHz, XTAL is in Hz */
+	ndiv = ( ((p->frequency * vco_div * 10) / (2 * XTAL / 1000)) / 32) & 0x1ff;
+	adiv = ( ((p->frequency * vco_div * 10) / (2 * XTAL / 1000)) % 32) & 0x1f;
 
 	if (adiv == 0)
-		adiv++;
+		ndiv++;
 
-	/* determine the correct pll frequency values. */
-	/* Command 11, refdiv 11, cpump polarity 1, cpump current 3mA 10. */
-	state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | (2 << 14);
-	state->pllarg |= (ndiv << 5) | adiv;
+	/* control bits 11, refdiv 11, charge pump polarity 1, charge pump current, ndiv, adiv */
+	state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | (pump << 14) | (ndiv << 5) | adiv;
 
 	return 0;
 }
@@ -538,6 +610,9 @@
 static int cx24123_pll_tune(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 {
 	struct cx24123_state *state = fe->demodulator_priv;
+	u8 val;
+
+	dprintk("frequency=%i\n", p->frequency);
 
 	if (cx24123_pll_calculate(fe, p) != 0) {
 		printk("%s: cx24123_pll_calcutate failed\n",__FUNCTION__);
@@ -552,6 +627,11 @@
 	cx24123_pll_writereg(fe, p, state->bandselectarg);
 	cx24123_pll_writereg(fe, p, state->pllarg);
 
+	/* set the FILTUNE voltage */
+	val = cx24123_readreg(state, 0x28) & ~0x3;
+	cx24123_writereg(state, 0x27, state->FILTune >> 2);
+	cx24123_writereg(state, 0x28, val | (state->FILTune & 0x3));
+
 	return 0;
 }
 
@@ -624,13 +704,81 @@
 	return 0;
 }
 
-static int cx24123_send_diseqc_msg(struct dvb_frontend* fe,
-				   struct dvb_diseqc_master_cmd *cmd)
+static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd)
 {
-	/* fixme: Implement diseqc */
-	printk("%s: No support yet\n",__FUNCTION__);
+	struct cx24123_state *state = fe->demodulator_priv;
+	int i, val;
+	unsigned long timeout;
+
+	dprintk("%s:\n",__FUNCTION__);
+
+	/* check if continuous tone has been stoped */
+	if (state->config->use_isl6421)
+		val = cx24123_readlnbreg(state, 0x00) & 0x10;
+	else
+		val = cx24123_readreg(state, 0x29) & 0x10;
+
+
+	if (val) {
+		printk("%s: ERROR: attempt to send diseqc command before tone is off\n", __FUNCTION__);
+		return -ENOTSUPP;
+	}
+
+	/* select tone mode */
+	cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xf8);
+
+	for (i = 0; i < cmd->msg_len; i++)
+		cx24123_writereg(state, 0x2C + i, cmd->msg[i]);
+
+	val = cx24123_readreg(state, 0x29);
+	cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40) | ((cmd->msg_len-3) & 3));
+
+	timeout = jiffies + msecs_to_jiffies(100);
+	while (!time_after(jiffies, timeout) && !(cx24123_readreg(state, 0x29) & 0x40))
+		; // wait for LNB ready
+
+	return 0;
+}
+
+static int cx24123_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+	struct cx24123_state *state = fe->demodulator_priv;
+	int val;
+	unsigned long timeout;
+
+	dprintk("%s:\n", __FUNCTION__);
+
+	/* check if continuous tone has been stoped */
+	if (state->config->use_isl6421)
+		val = cx24123_readlnbreg(state, 0x00) & 0x10;
+	else
+		val = cx24123_readreg(state, 0x29) & 0x10;
+
+
+	if (val) {
+		printk("%s: ERROR: attempt to send diseqc command before tone is off\n", __FUNCTION__);
+		return -ENOTSUPP;
+	}
+
+	/* select tone mode */
+	val = cx24123_readreg(state, 0x2a) & 0xf8;
+	cx24123_writereg(state, 0x2a, val | 0x04);
 
-	return -ENOTSUPP;
+	val = cx24123_readreg(state, 0x29);
+
+	if (burst == SEC_MINI_A)
+		cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x00));
+	else if (burst == SEC_MINI_B)
+		cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x08));
+	else
+		return -EINVAL;
+
+
+	timeout = jiffies + msecs_to_jiffies(100);
+	while (!time_after(jiffies, timeout) && !(cx24123_readreg(state, 0x29) & 0x40))
+		; // wait for LNB ready
+
+	return 0;
 }
 
 static int cx24123_read_status(struct dvb_frontend* fe, fe_status_t* status)
@@ -642,46 +790,57 @@
 
 	*status = 0;
 	if (lock & 0x01)
-		*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
+		*status |= FE_HAS_SIGNAL;
+	if (sync & 0x02)
+		*status |= FE_HAS_CARRIER;
 	if (sync & 0x04)
 		*status |= FE_HAS_VITERBI;
 	if (sync & 0x08)
-		*status |= FE_HAS_CARRIER;
+		*status |= FE_HAS_SYNC;
 	if (sync & 0x80)
-		*status |= FE_HAS_SYNC | FE_HAS_LOCK;
+		*status |= FE_HAS_LOCK;
 
 	return 0;
 }
 
-/*
- * Configured to return the measurement of errors in blocks, because no UCBLOCKS value
- * is available, so this value doubles up to satisfy both measurements
- */
-static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber)
+static void cx24123_read_error_rate(struct dvb_frontend* fe, u32* ber, u16* snr)
 {
 	struct cx24123_state *state = fe->demodulator_priv;
 
-	state->lastber =
+	u32 cur_ber = 
 		((cx24123_readreg(state, 0x1c) & 0x3f) << 16) |
 		(cx24123_readreg(state, 0x1d) << 8 |
 		cx24123_readreg(state, 0x1e));
 
+	if(ber)
+		 *ber = cur_ber;
+
 	/* Do the signal quality processing here, it's derived from the BER. */
 	/* Scale the BER from a 24bit to a SNR 16 bit where higher = better */
-	if (state->lastber < 5000)
-		state->snr = 655*100;
-	else if ( (state->lastber >=   5000) && (state->lastber <  55000) )
-		state->snr = 655*90;
-	else if ( (state->lastber >=  55000) && (state->lastber < 150000) )
-		state->snr = 655*80;
-	else if ( (state->lastber >= 150000) && (state->lastber < 250000) )
-		state->snr = 655*70;
-	else if ( (state->lastber >= 250000) && (state->lastber < 450000) )
-		state->snr = 655*65;
-	else
-		state->snr = 0;
+	if(snr)
+	{
+		if (cur_ber < 5000)
+			*snr = 655*100;
+		else if ( (cur_ber >=   5000) && (cur_ber <  55000) )
+			*snr = 655*90;
+		else if ( (cur_ber >=  55000) && (cur_ber < 150000) )
+			*snr = 655*80;
+		else if ( (cur_ber >= 150000) && (cur_ber < 250000) )
+			*snr = 655*70;
+		else if ( (cur_ber >= 250000) && (cur_ber < 450000) )
+			*snr = 655*65;
+		else
+			*snr = 0;
+	}
+}
 
-	*ber = state->lastber;
+/*
+ * Configured to return the measurement of errors in blocks, because no UCBLOCKS value
+ * is available, so this value doubles up to satisfy both measurements
+ */
+static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	cx24123_read_error_rate(fe, ber, NULL);
 
 	return 0;
 }
@@ -696,16 +855,14 @@
 
 static int cx24123_read_snr(struct dvb_frontend* fe, u16* snr)
 {
-	struct cx24123_state *state = fe->demodulator_priv;
-	*snr = state->snr;
+	cx24123_read_error_rate(fe, NULL, snr);
 
 	return 0;
 }
 
 static int cx24123_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
 {
-	struct cx24123_state *state = fe->demodulator_priv;
-	*ucblocks = state->lastber;
+	cx24123_read_error_rate(fe, ucblocks, NULL);
 
 	return 0;
 }
@@ -819,8 +976,6 @@
 	state->config = config;
 	state->i2c = i2c;
 	memcpy(&state->ops, &cx24123_ops, sizeof(struct dvb_frontend_ops));
-	state->lastber = 0;
-	state->snr = 0;
 	state->lnbreg = 0;
 	state->VCAarg = 0;
 	state->VGAarg = 0;
@@ -855,12 +1010,13 @@
 		.frequency_min = 950000,
 		.frequency_max = 2150000,
 		.frequency_stepsize = 1011, /* kHz for QPSK frontends */
-		.frequency_tolerance = 29500,
+		.frequency_tolerance = 5000,
 		.symbol_rate_min = 1000000,
 		.symbol_rate_max = 45000000,
 		.caps = FE_CAN_INVERSION_AUTO |
 			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
 			FE_CAN_QPSK | FE_CAN_RECOVER
 	},
 
@@ -875,6 +1031,7 @@
 	.read_snr = cx24123_read_snr,
 	.read_ucblocks = cx24123_read_ucblocks,
 	.diseqc_send_master_cmd = cx24123_send_diseqc_msg,
+	.diseqc_send_burst = cx24123_diseqc_send_burst,
 	.set_tone = cx24123_set_tone,
 	.set_voltage = cx24123_set_voltage,
 };
@@ -882,6 +1039,9 @@
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
+module_param(force_band, int, 0644);
+MODULE_PARM_DESC(force_band, "Force a specific band select (1-10, default:off).");
+
 MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24123/cx24109 hardware");
 MODULE_AUTHOR("Steven Toth");
 MODULE_LICENSE("GPL");
_______________________________________________

linux-dvb@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb

[Index of Archives]     [Linux Media]     [Video 4 Linux]     [Asterisk]     [Samba]     [Xorg]     [Xfree86]     [Linux USB]

  Powered by Linux