[linux-dvb] More on the cx24123 [PATCH]

Todd nospam at tippyturtle.com
Thu Apr 6 08:58:30 CEST 2006


Sweet.  Works...SMOOOOOTH like butter.  Just applied it to stock 2.6.16.1 
kernel.

...and to come clean about my setup...I am actually running cascaded 
switches: A twin LNB and an external switch to bring in a third LNB. 
Everything works great with this fix.  Yes, all 3 come in.

Todd

----- Original Message ----- 
From: "Yeasah Pell" <yeasah at schwide.com>
Cc: "Todd" <nospam at tippyturtle.com>; <linux-dvb at linuxtv.org>
Sent: Wednesday, April 05, 2006 4:25 PM
Subject: Re: [linux-dvb] More on the cx24123 [PATCH]


>I took a closer look at this, and I believe I have found the problem.
> The cx24123_send_diseqc_msg() function did not wait for the busy bit to
> be cleared before sending the message, only after. That behavior makes
> some intiutive sense, since the driver is the only one that should ever
> be sending messages through the card, but experiments show that
> nevertheless the diseqc queue is sometimes marked as busy at the time
> the diseqc command is sent, and previously the driver would just blast
> the message through anyway, and it would presumably be lost. Some other
> condition must cause the queue to become busy -- it looks like changing
> the LNB voltage or tone status might do this. This is certainly why
> adding extra delay made my positioner more reliable -- I assume it also
> applied to switches, but I must have not noticed for some reason.
>
> I've revised the patch to have the driver wait for the busy bit both
> before and after sending a disqec message.
>
> Yeasah Pell wrote:
>
>> From what I've seen, some DiSEqC switches can be *really* picky, and
>> I've seen lots of reports of switches working fine on one card but not
>> working at all on another (or working unreliably) I can't offer any
>> more detail or explanation than that though -- just my observation of
>> reports online.
>>
>> My understanding is that the main difference between DiSEqC 1.x and
>> 2.0 is that 2.0 is bidirectional, which this card does seem to
>> support, but this driver doesn't. But that should just mean you won't
>> get status back from the devices -- the control aspect ought to still
>> work fine. You wouldn't get status back unless the disqec commands
>> were set to request it anyway, which the software you are using
>> probably isn't doing.
>>
>> I can confirm that on my DVB-S 100, with either my patch or vadim's
>> original diseqc patch, I am able to control a 4 port generic diseqc
>> switch which claims to be "1.0/2.0" compliant, as well as a dish
>> positioner.
>>
>> One thing you might try is enabling debug messages in the cx24123
>> module (i.e. by putting a line saying "options cx24123 debug=1" in
>> /etc/modprobe.conf and reloading the modules or rebooting), that way
>> you should see confirmation messages in dmesg of the diseqc messages
>> being sent. That will at least confirm that 1) you are definitely
>> loading the patched driver and 2) diseqc commands are in fact being
>> issued to the card. You should see some lines including the text
>> "cx24123_send_diseqc_msg", and hopefully no error indications.
>>
>> The only other thing I would note is that in order to get my
>> positioner to accept disqec commands reliably I had to increase the
>> delay between the tone shutoff/voltage change and the diseqc command
>> being sent in the software I use. I didn't have that problem with the
>> switch, but I certainly would put that sort of timing issue high up on
>> the list of suspected causes -- this card might take longer than
>> normal for its LNB voltage to stabilize, or to actually stop sending
>> the tone.
>>
>> The popular timing seems to be to wait 15 msec, I upped that to 100
>> msec to get it to work with my positioner. It probably doesn't need
>> that much of an increase, that's just what I tried first, and it
>> doesn't really add a significant amount of time to the total command
>> sequence from a user perspective. I was using other software, but if
>> you want to try the change in dvbscan, change the first msleep(15) in
>> the diseqc_send_msg function in diseqc.c to msleep(100), and recompile
>> -- in fact you could try lengthening all the delays in that function,
>> it certainly can't hurt to wait longer on any of those steps (apart
>> from making the command sequence take longer)
>>
>> If you find that adding delays helps, please let us know, as it means
>> we should probably put some delays into the driver ioctls.
>>
>> Todd wrote:
>>
>>> 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
>>
>>
>>
>>
>> _______________________________________________
>> linux-dvb mailing list
>> linux-dvb at linuxtv.org
>> http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb
>
>
>


--------------------------------------------------------------------------------


> --- linux/drivers/media/dvb/frontends/cx24123.c.orig 2006-04-05 
> 19:14:04.000000000 -0400
> +++ linux/drivers/media/dvb/frontends/cx24123.c 2006-04-05 
> 19:18:46.000000000 -0400
> @@ -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,92 @@
> {
>  u32 freq_low;
>  u32 freq_high;
> - u32 bandselect;
>  u32 VCOdivider;
> - u32 VCOnumber;
>  u32 progdata;
> } cx24123_bandselect_vals[] =
> {
> + // band 1
>  {
>  .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,
>  },
> +
> + // band 2
>  {
>  .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,
>  },
> +
> + // band 3
>  {
> - .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,
>  },
> +
> + // band 4
>  {
> - .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,
>  },
> +
> + // band 5
>  {
> - .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,
>  },
> +
> + // band 6
>  {
> - .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,
>  },
> +
> + // band 7
>  {
>  .freq_low = 1718000,
>  .freq_high = 1855999,
> - .bandselect = 0x20,
>  .VCOdivider = 2,
> - .VCOnumber = 6,
> - .progdata = (0 << 18) | (1 << 9) | 0x20,
> + .progdata = (0 << 19) | (1 << 9) | 0x10,
>  },
> +
> + // band 8
>  {
>  .freq_low = 1856000,
>  .freq_high = 2035999,
> - .bandselect = 0x40,
>  .VCOdivider = 2,
> - .VCOnumber = 7,
> - .progdata = (0 << 18) | (1 << 9) | 0x40,
> + .progdata = (0 << 19) | (1 << 9) | 0x20,
>  },
> +
> + // band 9
>  {
>  .freq_low = 2036000,
> - .freq_high = 2149999,
> - .bandselect = 0x80,
> + .freq_high = 2150000,
> + .VCOdivider = 2,
> + .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,
> - .VCOnumber = 8,
> - .progdata = (0 << 18) | (1 << 9) | 0x80,
> + .progdata = (0 << 19) | (1 << 9) | 0x80,
>  },
> +#endif
> };
>
> static struct {
> @@ -207,49 +197,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 +298,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 +336,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;
>
> - val = (srate / 1185) * 100;
> + /*  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
> +
> +     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
> + */
> +
> + tmp = srate << 6;
> + ratio = tmp / sample_rate;
> +
> + tmp = (tmp % sample_rate) << 6;
> + ratio = (ratio << 6) + (tmp / sample_rate);
> +
> + tmp = (tmp % sample_rate) << 6;
> + ratio = (ratio << 6) + (tmp / sample_rate);
>
> - /* Compensate for scaling up, by removing 17 symbols per 1Msps */
> - val = val - (17 * (srate / 1000000));
> + tmp = (tmp % sample_rate) << 5;
> + ratio = (ratio << 5) + (tmp / sample_rate);
>
> - cx24123_writereg(state, 0x08, (val >> 16) & 0xff );
> - cx24123_writereg(state, 0x09, (val >>  8) & 0xff );
> - cx24123_writereg(state, 0x0a, (val      ) & 0xff );
> +
> + 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 +513,9 @@
>  struct cx24123_state *state = fe->demodulator_priv;
>  u32 ndiv = 0, adiv = 0, vco_div = 0;
>  int i = 0;
> + int pump = 2;
> + int band = 0;
> + int num_bands = sizeof(cx24123_bandselect_vals) / 
> sizeof(cx24123_bandselect_vals[0]);
>
>  /* Defaults for low freq, low rate */
>  state->VCAarg = cx24123_AGC_vals[0].VCAprogdata;
> @@ -444,38 +523,50 @@
>  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 < 1 || force_band > num_bands)
>  {
> - 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 < num_bands; i++)
> + {
> + if ((cx24123_bandselect_vals[i].freq_low <= p->frequency) &&
> +     (cx24123_bandselect_vals[i].freq_high >= p->frequency) )
> + band = i;
>  }
>  }
> + else
> + band = force_band - 1;
> +        dprintk("bandselect=%i\n", band+1);
> +
> + 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 +629,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 +646,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 +723,87 @@
>  return 0;
> }
>
> -static int cx24123_send_diseqc_msg(struct dvb_frontend* fe,
> -    struct dvb_diseqc_master_cmd *cmd)
> +static void cx24123_wait_for_diseqc(struct cx24123_state *state)
> {
> - /* fixme: Implement diseqc */
> - printk("%s: No support yet\n",__FUNCTION__);
> + unsigned long timeout = jiffies + msecs_to_jiffies(100);
> + while (!time_after(jiffies, timeout) && !(cx24123_readreg(state, 0x29) & 
> 0x40))
> + ; // wait for LNB ready
> +}
> +
> +static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, struct 
> dvb_diseqc_master_cmd *cmd)
> +{
> + struct cx24123_state *state = fe->demodulator_priv;
> + int i, val;
> +
> + dprintk("%s:\n",__FUNCTION__);
> +
> + /* check if continuous tone has been stopped */
> + 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;
> + }
> +
> + /* wait for diseqc queue ready */
> + cx24123_wait_for_diseqc(state);
>
> - 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));
> +
> + /* wait for diseqc message to finish sending */
> + cx24123_wait_for_diseqc(state);
> +
> + 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;
> +
> + 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;
> + }
> +
> + cx24123_wait_for_diseqc(state);
> +
> + /* select tone mode */
> + val = cx24123_readreg(state, 0x2a) & 0xf8;
> + cx24123_writereg(state, 0x2a, val | 0x04);
> +
> + 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;
> +
> + cx24123_wait_for_diseqc(state);
> +
> + return 0;
> }
>
> static int cx24123_read_status(struct dvb_frontend* fe, fe_status_t* 
> status)
> @@ -642,46 +815,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 +880,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 +1001,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 +1035,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 +1056,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 +1064,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");
> 




More information about the linux-dvb mailing list