[linux-dvb] More on the cx24123

Todd nospam at tippyturtle.com
Wed Apr 5 08:23:25 CEST 2006


I applied the your attached "cx24123.patch" to the 2.6.16.1 Kernel (it 
"took" just fine).  I still can't get the DiSEqC  functionality to work.  I 
am using dvbscan from here:
http://www.phobos.ca/dvb/
...on some DishPro switches (supposedly DiSEqC  2.0 switches).  It works 
fine with my "Sky2PC/SkyStar 2 DVB-S" card, but not this kworld card. (Same 
tools, same commands; literally cut and pasted between SSH windows...even 
switch cabling into switch)

Is this patch dated?...is there a new one?  Does the kworld card support 
DiSEqC  2.0?  (the marketing hype claims 2.x compatibility: 
http://www.kworld.com.tw/en/product/dvb-s/001/dvb-s100.htm )

Thanks in advance,
Todd

----- Original Message ----- 
From: "Yeasah Pell" <yeasah at schwide.com>
To: <linux-dvb at linuxtv.org>
Sent: Friday, March 24, 2006 12:16 PM
Subject: Re: [linux-dvb] More on the cx24123


> >
>>
>>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 mailing list
> linux-dvb at linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb 




More information about the linux-dvb mailing list