Mailing List archive

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

[linux-dvb] Re: Pinnacle pctv sat



Am Son, 2003-04-20 um 17.14 schrieb Volker Cordes:
> It would be nice if you could tell me what you did to compile the 
> drivers (which kernel, patches, etc) and send me your frontend source so 
> that I can try. Also I would like to know which program you use.

Please find attached my cx24211.c port. I've also attached the load
script to load the modules. I'm using a SuSE 2.4.19 kernel.

To test it I'm using vdr with a xine plugin. This code I can't release
now, because it needs some patching in xine and a special set up.

I let it run some hours now and it worked reliable so far (except that
the xine plugin was hanging, because of a pts problem).

Mike


/* 
    cx24110 - Single Chip Satellite Channel Receiver driver module
               used on the the Pinnacle PCTV Sat cards

    Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on
    work
    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>

    Rewritten for dvb-kernel by Mike Pieper <mike@pieper-family.de>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/    

/* currently drives the Conexant cx24110 and cx24106 QPSK decoder chips, 
   connected via i2c to a Conexant Fusion 878 (this uses the standard
   linux bttv driver). The tuner chip is supposed to be the Conexant
   cx24108 digital satellite tuner, driven through the tuner interface
   of the cx24110. SEC is also supplied by the cx24110.
   
   This module contains code to control the demodulator, the tuner and
   the LNC (SEC commands).

   Version for the "oldstruct" branch of linux-dvb, 26-Apr-2002
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <linux/i2c.h>

#include "dvb_frontend.h"


static int debug = 0;
#define dprintk	if (debug) printk

static
struct dvb_frontend_info cx24_info = {
	name: "Conexant cx24110/cx24106 frontend",
	type: FE_QPSK,
	frequency_min: 950000,
	frequency_max: 2150000,
	frequency_stepsize: 250,           /* kHz for QPSK frontends */
	frequency_tolerance: 29500,
	symbol_rate_min: 500000,
	symbol_rate_max: 90999000UL/2,
/*      symbol_rate_tolerance: ???,*/
	notifier_delay: 50,                /* 1/20 s */
	caps:   FE_CAN_INVERSION_AUTO | 
	FE_CAN_FEC_AUTO |
	FE_CAN_QPSK
};

struct cx24110 {
        u32 srate;
        u32 freq;
        u8 values_changed;
};

static 
int cx24_writereg(struct dvb_i2c_bus *i2c, int reg, int data)
{
	int ret;
	u8 buf [] = { reg, data };
	struct i2c_msg msg = { addr: 0x55, flags: 0, buf: buf, len: 2 };

	// dprintk ("%s\n", __FUNCTION__);

	// dprintk("cx24110 debug: writereg(%2.2x,%2.2x)\n",reg,data);

	ret = i2c->xfer (i2c, &msg, 1);

	if (ret != 1) 
		dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
			__FUNCTION__, reg, data, ret);

	return (ret != 1) ? -1 : 0;
}

static
u8 cx24_readreg(struct dvb_i2c_bus *i2c, u8 reg)
{
	int ret;
	u8 b0 [] = { reg };
	u8 b1 [] = { 0 };
	struct i2c_msg msg [] = { { addr: 0x55, flags: 0, buf: b0, len: 1 },
			   { addr: 0x55, flags: I2C_M_RD, buf: b1, len: 1 } };

	// dprintk ("%s\n", __FUNCTION__);

	ret = i2c->xfer (i2c, msg, 2);
        
	if (ret != 2) 
		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);

	// dprintk("cx24110 debug: readreg(%2.2x)=%2.2x\n",reg,b1[0]);

	return b1[0];
}

#if 0
static int dump(struct i2c_client *client)
{
        int i;
        
        printk("cx24110: DUMP\n");
        
        for (i=0; i<0x7e; i++) 
        {
                printk("%02x ", readreg(client, i));
                if ((i&7)==7)
                        printk("\n");
        }
        printk("\n");
        return 0;
}




static int SetSecCommand(struct i2c_client *client, u8 *msg)
{
        int len=msg[0]&0x3f;
        u8 *mp=msg+1;
        int i, plen, rv;
	int t;
        
        dprintk("cx24110 debug: SendSecCmd, len=0x%02x [",msg[0]);
        for(i=0;i<len;i++) dprintk("%02x ",msg[i+1]);
        dprintk("]\n");
        rv=readreg(client,0x76);

        if(len>0) {
           for(;len>0;len-=6) {
              if(len<0) len=0;
              plen=(len>6)?6:len;
              for(i=0;i<plen;i++) {
                 writereg(client,0x79+i,*mp++);
              }
              plen-=3; if(plen<0) plen=0;
              if(len>6) {
                 writereg(client,0x76,(rv&0x90)| /* keep DC & cont tone bits */
                                 0x60| /* go, long message */
                                 (plen&3));
              } else {
                 writereg(client,0x76,(t=(rv&0x90)| /* keep DC & cont tone bits */
                                 0x40| /* go, no long msg */
//                                 ((msg[0]&0x80)?0:0x04)| /* last msg, send burst */
                                 (msg[0]&0x40)>>3| /* burst value */
                                 (plen&3)));
              }
              for(i=500;i-->0&&!(readreg(client,0x76)&0x40);); /* wait for LNB ready */
           }
        } else {
           writereg(client,0x76,(rv&0x90)| /* keep DC & cont tone bits */
                           0x40| /* go, no long msg */
//                           ((msg[0]&0x80)?0:0x04)| /* last msg, send burst? */
                           (msg[0]&0x40)>>3); /* burst value */
                   }
        return 0;
}


static int dvb_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
        struct cx24110 *cx=(struct cx24110 *) client->data;

        switch (cmd) 
        {
        case FE_READ_STATUS:
	{
		FrontendStatus *status=(FrontendStatus *) arg;
		int sync;

		*status=0;

                sync=readreg(client,0x55);
		dprintk("cx24110 debug: read_status reg55=%2.2x\n",sync);
		if (sync&0x10)
			*status|=FE_HAS_SIGNAL;
		if (sync&0x08)
			*status|=FE_HAS_CARRIER;
		sync=readreg(client,0x08);
		dprintk("cx24110 debug: read_status reg08=%2.2x\n",sync);
		if (sync&0x40)
			*status|=FE_HAS_VITERBI;
		if (sync&0x20)
			*status|=FE_HAS_SYNC;
		if ((sync&0x60)==0x60)
			*status|=FE_HAS_LOCK;

                sync=readreg(client,0x22);
		dprintk("cx24110 debug: read_status reg22=%2.2x\n",sync);
		if (sync&0x10)
			*status|=FE_SPECTRUM_INV;
		if(!(*status&FE_HAS_LOCK)) {
			sync=readreg(client,0x04);
                	dprintk("cx24110 debug: read_status reg04=%2.2x\n",sync);
                	if(sync&0x10)
                		*status|=FE_DID_NOT_LOCK;
		}
		break;
	}
        case FE_READ_BER:
	{
		u32 *ber=(u32 *) arg;

		writereg(client,0x10,0xa0); /* select reading of RS Byte Error Count */
		*ber = readreg(client,0x12);
                *ber|=(readreg(client,0x13)<<8);
                *ber|=(readreg(client,0x14)<<16);
		break;
	}
        case FE_READ_SIGNAL_STRENGTH:
	{
		s32 *signal=(s32 *) arg;

                *signal=(signed char)readreg(client,0x27); /* the AGC Acc determines the total gain in the tuner... */
                /* this is NOT a calibrated value! */
		break;
	}
        case FE_READ_SNR:
	{
		s32 *snr=(s32 *) arg;

                *snr=(readreg(client,0x68)<<8)|readreg(client,0x69);
                /* this is a "Es/N0 estimator count". I do not know how to
                   compute snr from it... leave the old computation in place */
		*snr=20000000+(10-(*snr>>8))*20000000/160;
		break;
	}
	case FE_READ_UNCORRECTED_BLOCKS: 
	{
		u32 *ublocks=(u32 *) arg;
		writereg(client,0x10,0xb0); /* select reading of RS Byte Error Count */
		*ublocks = readreg(client,0x12);
                *ublocks|=(readreg(client,0x13)<<8);
                *ublocks|=(readreg(client,0x14)<<16);
		break;
	}
        case FE_READ_AFC:
	{
		s32 *afc=(s32 *) arg;
		
                *afc=((int)((signed char)(readreg(client,0x47)<<4)|(readreg(client,0x48)&0x0f)));
                *afc=(*afc*(int)(cx->srate/(1<<14)));
                /* again the problem that intermediate results might need more
                   than 32 bits. I think we can live with reduced accuracy here */
                /* do we need to take the FEDR value into account? */
		break;
	}
        case FE_GET_INFO:
	{
		FrontendInfo *feinfo=(FrontendInfo *) arg;

		feinfo->type=FE_QPSK;
		feinfo->minFrequency=950000; /* kHz */ 
		feinfo->maxFrequency=2150000;
		feinfo->minSymbolRate=500000;
		feinfo->maxSymbolRate=90999000UL/2;
		feinfo->hwType=0;    
		feinfo->hwVersion=0;
		break;
	}
                
        case FE_WRITEREG:
        {
                u8 *msg = (u8 *) arg;
                writereg(client, msg[0], msg[1]);
                break;
        }
        case FE_READREG:
        {
                u8 *msg = (u8 *) arg;
                msg[1]=readreg(client, msg[0]);

                break;
        }
        case FE_INIT:
        {
                init(client);
                writereg(client,0x76,0x50);
                break;
        }
        case FE_SET_FRONTEND:
        {
		FrontendParameters *param = (FrontendParameters *) arg;

		SetInversion(client, param->Inversion);
                SetFEC(client, param->u.qpsk.FEC_inner);
                SetSymbolrate(client, param->u.qpsk.SymbolRate);
                if(((struct cx24110 *)(client->data))->values_changed) {
                   writereg(client,0x04,0x05); /* start Acquisition */
                }
                ((struct cx24110 *)(client->data))->values_changed=0;
                break;
        }
	case TUNER_SET_TVFREQ:
	{
		int i=0; u32 srate;
		dprintk("cx24110 debug: set_tvfreq %d\n",*(int *)arg);
                SetTunerFreq(client, *(int *)arg);
		while(!(readreg(client,0x66)&0x20)&&i<1000) i++;		
		dprintk("cx24110 debug: GPIO IN=%2.2x(loop=%d)\n",readreg(client,0x66),i);
		srate=cx->srate; cx->srate++;
		SetSymbolrate(client,srate);
                writereg(client,0x04,0x05);
                break;
	}
	case SEC_SET_VOLTAGE:
	{
		int oldval=readreg(client,0x76)&0x3f;
		
		if(*(int *)arg==0 /* 13 Volt */) {
		   writereg(client,0x76,oldval|0xc0);
		} else if(*(int *)arg==1 /* 18 Volt */) {
		   writereg(client,0x76,oldval|0x40);
		} else return -EINVAL;
		break;
	}
	case SEC_SET_TONE:
	{
		int oldval=readreg(client,0x76)&0xaf;
		
		if(*(int *)arg==0 /* no tone */) {
		   writereg(client,0x76,oldval|0x40);
		} else if(*(int *)arg==1 /* tone */) {
		   writereg(client,0x76,oldval|0x50);
		} else return -EINVAL;
		break;
	}
        case SEC_SEND_SEQUENCE:
        {
                SetSecCommand(client,(u8 *)arg);
                break;
        }
        case FE_RESET:
        {
                break;
        }
        default:
                return -1;
        }
        return 0;
} 
static struct i2c_driver cx24_driver = {
        "cx24110 DVB demodulator",
        I2C_DRIVERID_CX24110,
        I2C_DF_NOTIFY,
        attach_adapter,
        detach_client,
        dvb_command,
        inc_use,
        dec_use,
};

static struct i2c_client client_template = {
        name:    "cx24110",
        id:      I2C_DRIVERID_CX24110,
        flags:   0,
        addr:    (0x55),
        adapter: NULL,
        driver:  &cx24_driver,
};

#endif

static 
int cx24_init(struct dvb_i2c_bus *i2c, struct cx24110 *cx)
{
        int i;
        struct {u8 reg; u8 data;} regdata[]=
                      /* Comments beginning with @ denote this value should
                         be the default */
        {{0x09,0x01}, /* SoftResetAll */
         {0x09,0x00}, /* release reset */
         {0x01,0xe8}, /* MSB of code rate 27.5MS/s */
         {0x02,0x17}, /* middle byte " */
         {0x03,0x29}, /* LSB         " */
         {0x05,0x03}, /* @ DVB mode, standard code rate 3/4 */
         {0x06,0xa5}, /* @ PLL 60MHz */
         {0x07,0x01}, /* @ Fclk, i.e. sampling clock, 60MHz */
         {0x0a,0x00}, /* @ partial chip disables, do not set */
         {0x0b,0x01}, /* set output clock in gapped mode, start signal low
                         active for first byte */
         {0x0c,0x11}, /* no parity bytes, large hold time, serial data out */
         {0x0d,0x6f}, /* @ RS Sync/Unsync thresholds */
         {0x10,0xc0}, /* chip doc is misleading here: write bit 6 as 1
                         to avoid starting the BER counter. Reset the 
                         CRC test bit. Infinite counting selected */
         {0x15,0xff}, /* @ size of the limited time window for RS BER
                         estimation. It is <value>*256 RS blocks, this
                         gives approx. 2.6 sec at 27.5MS/s, rate 3/4 */
         {0x16,0x00}, /* @ enable all RS output ports */
         {0x17,0x04}, /* @ time window allowed for the RS to sync */
         {0x18,0xae}, /* @ allow all standard DVB code rates to be scanned
                         for automatically */
                      /* leave the current code rate and normalization
                         registers as they are after reset... */
         {0x21,0x10}, /* @ during AutoAcq, search each viterbi setting
                         only once */
         {0x23,0x18}, /* @ size of the limited time window for Viterbi BER          
                         estimation. It is <value>*65536 channel bits, i.e.
                         approx. 38ms at 27.5MS/s, rate 3/4 */
         {0x24,0x28}, /* do not trigger Viterbi CRC test. Infinite count window */
         	      /* leave front-end AGC parameters at default values */
         	      /* leave decimation AGC parameters at default values */
         {0x35,0x40}, /* disable all interrupts. They are not connected anyway */
         {0x36,0xff}, /* clear all interrupt pending flags */
         {0x37,0x00}, /* @ fully enable AutoAcqq state machine */
         {0x38,0x07}, /* @ enable fade recovery, but not autostart AutoAcq */
                      /* leave the equalizer parameters on their default values */
                      /* leave the final AGC parameters on their default values */
	 {0x41,0x00}, /* @ MSB of front-end derotator frequency */
	 {0x42,0x00}, /* @ middle bytes " */
	 {0x43,0x00}, /* @ LSB          " */
	 	      /* leave the carrier tracking loop parameters on default */
	 	      /* leave the bit timing loop parameters at gefault */
	 {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by
	                 the cx24108 data sheet for symbol rates above 15MS/s */
	 {0x57,0x00}, /* @ Filter sigma delta enabled, positive */
	 {0x61,0x95}, /* GPIO pins 1-4 have special function */
	 {0x62,0x05}, /* GPIO pin 5 has special function, pin 6 is GPIO */
	 {0x63,0x00}, /* All GPIO pins use CMOS output characteristics */
	 {0x64,0x20}, /* GPIO 6 is input, all others are outputs */
	 {0x6d,0x30}, /* tuner auto mode clock freq 62kHz */
	 {0x70,0x15}, /* use auto mode, tuner word is 21 bits long */
	 {0x73,0x00}, /* @ disable several demod bypasses */
	 {0x74,0x00}, /* @  " */
	 {0x75,0x00}  /* @  " */
	 	      /* the remaining registers are for SEC */
	};
        
        dprintk("cx24110: init chip\n");

	for(i=0;i<sizeof(regdata)/sizeof(regdata[0]);i++) {
	   cx24_writereg(i2c,regdata[i].reg,regdata[i].data);
	};

        cx->srate=27500001;

        return 0;
}

static int write_tuner(struct dvb_i2c_bus *i2c, u32 progword)
{
	dprintk("cx24110 debug: write_tuner(%8.8x)\n",progword);

	cx24_writereg(i2c,0x6d,0x30); /* auto mode at 62kHz */
	cx24_writereg(i2c,0x70,0x15); /* auto mode 21 bits */
	/* if the auto tuner writer is still busy, clear it out */
	while(cx24_readreg(i2c,0x6d)&0x80) cx24_writereg(i2c,0x72,0);
	/* write the topmost 8 bits */
	cx24_writereg(i2c,0x72,(progword>>24)&0xff);
	/* wait for the send to be completed */
	while((cx24_readreg(i2c,0x6d)&0xc0)==0x80);
	/* send another 8 bytes */
	cx24_writereg(i2c,0x72,(progword>>16)&0xff);
	while((cx24_readreg(i2c,0x6d)&0xc0)==0x80);
	/* and the topmost 5 bits of this byte */
	cx24_writereg(i2c,0x72,(progword>>8)&0xff);
	while((cx24_readreg(i2c,0x6d)&0xc0)==0x80);
	/* now strobe the enable line once */
	cx24_writereg(i2c,0x6d,0x32);
	cx24_writereg(i2c,0x6d,0x30);
	
	return 0;
}

static inline void ddelay(int i) 
{
        current->state=TASK_INTERRUPTIBLE;
        schedule_timeout((HZ*i)/100);
}

static int SetTunerFreq(struct dvb_i2c_bus *i2c, struct cx24110 *cx, u32 freq)
{
	int i, a, n, pump; 
	u32 band, pll;

	
	u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000,
	           1576000,1718000,1856000,2036000,2150000};
	u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000,
	           0x00102000,0x00104000,0x00108000,0x00110000,
	           0x00120000,0x00140000};
	           
#define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */
	dprintk("cx24110 debug: entering SetTunerFreq, freq=%d\n",freq);
	
	/* This is really the bit driving the tuner chip cx24108 */
	
	//freq/=1000; /* change units to kHz */
	if(freq==cx->freq) return 0;
	if(freq<950000) freq=950000; /* kHz */
	if(freq>2150000) freq=2150000; /* satellite IF is 950..2150MHz */
	cx->freq=freq;
	
	/* decide which VCO to use for the input frequency */
	for(i=1;(i<sizeof(osci)/sizeof(osci[0]))&&(osci[i]<cx->freq);i++);
	dprintk("cx24110 debug: select vco #%d (f=%d)\n",i,cx->freq);
	band=bandsel[i];
	/* the gain values must be set by SetSymbolrate */
	/* compute the pll divider needed, from Conexant data sheet,
	   resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
	   depending on the divider bit. It is set to /4 on the 2 lowest 
	   bands  */
	n=((i<=2?2:1)*freq*10L)/(XTAL/100);
	a=n%32; n/=32; if(a==0) n--;
	pump=(freq<(osci[i-1]+osci[i])/2);
	pll=0xf8000000|
	    ((pump?1:2)<<(14+11))|
	    ((n&0x1ff)<<(5+11))|
	    ((a&0x1f)<<11);
	/* everything is shifted left 11 bits to left-align the bits in the
	   32bit word. Output to the tuner goes MSB-aligned, after all */
	dprintk("cx24110 debug: pump=%d, n=%d, a=%d\n",pump,n,a);
	write_tuner(i2c,band);
	/* set vga and vca to their widest-band settings, as a precaution.
	   SetSymbolrate might not be called to set this up */
	write_tuner(i2c,0x500c0000);
	write_tuner(i2c,0x83f1f800);
	write_tuner(i2c,pll);
	cx24_writereg(i2c,0x56,0x7f);
	ddelay(HZ/10); /* wait a moment for the tuner pll to lock */	
	return 0;
}

static int SetSymbolrate(struct dvb_i2c_bus *i2c, struct cx24110 *cx, u32 srate)
{
        u32 ratio;
	u32 tmp, fclk, BDRI;

	u32 bands[]={5000000UL,15000000UL,90999000UL/2};
	u32 vca[]={0x80f03800,0x81f0f800,0x83f1f800};
	u32 vga[]={0x5f8fc000,0x580f0000,0x500c0000};
	u8  filtune[]={0xa2,0xcc,0x66};
	int i;

/*dprintk("cx24110 debug: entering SetSymbolrate(%d)\n",srate);	*/
        if (cx->srate==srate) {
                return 0;
        }
        dprintk("setsymbolrate %d\n", srate);

	if (srate>90999000UL/2)
                srate=90999000UL/2;
        if (srate<500000)
                srate=500000;
        cx->srate=srate;
        
        for(i=0;(i<sizeof(bands)/sizeof(bands[0]))&&(srate>bands[i]);i++);

        /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz,
           and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult,
           R06[3:0] PLLphaseDetGain */
        tmp=cx24_readreg(i2c,0x07)&0xfc;
        if(srate<90999000UL/4) { /* sample rate 45MHz*/
           cx24_writereg(i2c,0x07,tmp);
           cx24_writereg(i2c,0x06,0x78);
           fclk=90999000UL/2;
        } else if(srate<60666000UL/2) { /* sample rate 60MHz */
	   cx24_writereg(i2c,0x07,tmp|0x1);
	   cx24_writereg(i2c,0x06,0xa5);
	   fclk=60666000UL;
        } else if(srate<80888000UL/2) { /* sample rate 80MHz */
	   cx24_writereg(i2c,0x07,tmp|0x2);
	   cx24_writereg(i2c,0x06,0x87);
	   fclk=80888000UL;
        } else { /* sample rate 90MHz */
	   cx24_writereg(i2c,0x07,tmp|0x3);
	   cx24_writereg(i2c,0x06,0x78);
	   fclk=90999000UL;
        };
        dprintk("cx24110 debug: fclk %d Hz\n",fclk);
        /* we need to divide two integers with approx. 27 bits in 32 bit
           arithmetic giving a 25 bit result */
	/* the maximum dividend is 90999000/2, 0x02b6446c, this number is
	   also the most complicated divisor. Hence, the dividend has,
	   assuming 32bit unsigned arithmetic, 6 clear bits on top, the 
	   divisor 2 unused bits at the bottom. Also, the quotient is
	   always less than 1/2. Borrowed from VES1893.c, of course */

        tmp=srate<<6;
        BDRI=fclk>>2;
	ratio=(tmp/BDRI);
        
	tmp=(tmp%BDRI)<<8;
	ratio=(ratio<<8)+(tmp/BDRI);
        
	tmp=(tmp%BDRI)<<8;
	ratio=(ratio<<8)+(tmp/BDRI);

	tmp=(tmp%BDRI)<<1;
	ratio=(ratio<<1)+(tmp/BDRI);
        
        dprintk("srate= %d (range %d, up to %d)\n", srate,i,bands[i]);
        dprintk("fclk = %d\n", fclk);
        dprintk("ratio= %08x\n", ratio);

        cx24_writereg(i2c, 0x1, (ratio>>16)&0xff);
	cx24_writereg(i2c, 0x2, (ratio>>8)&0xff);
	cx24_writereg(i2c, 0x3, (ratio)&0xff);

	/* please see the cx24108 data sheet, this controls tuner gain 
	   and bandwidth settings depending on the symbol rate */
	write_tuner(i2c,vga[i]);
	write_tuner(i2c,vca[i]); /* gain is set on tuner chip */
	cx24_writereg(i2c,0x56,filtune[i]); /* bw is contolled by filtune voltage */
	
	cx->values_changed=1;
	return 0;
}

static
int pll_set_tv_freq (struct dvb_i2c_bus *i2c, struct cx24110 *cx, u32 freq)
{
		int i=0; u32 srate;
		dprintk("cx24110 debug: set_tvfreq %u\n",freq);
                SetTunerFreq(i2c, cx, freq);
		while(!(cx24_readreg(i2c,0x66)&0x20)&&i<1000) i++;
		dprintk("cx24110 debug: GPIO IN=%2.2x(loop=%d)\n",
			cx24_readreg(i2c,0x66),i);
		srate=cx->srate; cx->srate++;
		SetSymbolrate(i2c,cx,srate);
                // cx24_writereg(i2c,0x04,0x05);
}

static
int cx24_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
	struct dvb_i2c_bus *i2c = fe->i2c;
	struct cx24110 *cx = (struct cx24110*)fe->data;
        switch (cmd) {
        case FE_GET_INFO:
		memcpy (arg, &cx24_info, sizeof(struct dvb_frontend_info));
		break;

        case FE_READ_STATUS:
	{
		fe_status_t *status = arg;
		int sync;

		*status=0;

                sync=cx24_readreg(i2c,0x55);
		// dprintk("cx24110 debug: read_status reg55=%2.2x\n",sync);
		if (sync&0x10)
			*status|=FE_HAS_SIGNAL;
		if (sync&0x08)
			*status|=FE_HAS_CARRIER;
		sync=cx24_readreg(i2c,0x08);
		// dprintk("cx24110 debug: read_status reg08=%2.2x\n",sync);
		if (sync&0x40)
			*status|=FE_HAS_VITERBI;
		if (sync&0x20)
			*status|=FE_HAS_SYNC;
		if ((sync&0x60)==0x60)
			*status|=FE_HAS_LOCK;

#if 0
		if(!(*status&FE_HAS_LOCK)) {
			sync=cx24_readreg(i2c,0x04);
                	dprintk("cx24110 debug: read_status reg04=%2.2x\n",sync);
                	if(sync&0x10)
                		*status|=FE_DID_NOT_LOCK;
		}
#endif
		dprintk ("cx24110 debug: Status%s%s%s%s%s\n",
			 *status&FE_HAS_SIGNAL?" FE_HAS_SIGNAL":"",
			 *status&FE_HAS_CARRIER?" FE_HAS_CARRIER":"",
			 *status&FE_HAS_VITERBI?" FE_HAS_VITERBI":"",
			 *status&FE_HAS_SYNC?" FE_HAS_SYNC":"",
			 *status&FE_HAS_LOCK?" FE_HAS_LOCK":"");
			 
		break;
	}

        case FE_READ_BER:
	{
		u32 *ber=(u32 *) arg;

		cx24_writereg(i2c,0x10,0xa0); /* select reading of RS Byte Error Count */
		*ber = cx24_readreg(i2c,0x12);
                *ber|=(cx24_readreg(i2c,0x13)<<8);
                *ber|=(cx24_readreg(i2c,0x14)<<16);
		break;
	}

        case FE_READ_SIGNAL_STRENGTH:
	{
		s32 *signal=(s32 *) arg;

                *signal=(signed char)cx24_readreg(i2c,0x27); /* the AGC Acc determines the total gain in the tuner... */
                /* this is NOT a calibrated value! */
		break;
	}

        case FE_READ_SNR:
	{
		u16 *snr=(u16 *) arg;

                *snr=(cx24_readreg(i2c,0x68)<<8)|cx24_readreg(i2c,0x69);
                /* this is a "Es/N0 estimator count". I do not know how to
                   compute snr from it... leave the old computation in place */
		/* *snr=20000000+(10-(*snr>>8))*20000000/160; */
		break;
	}

	case FE_READ_UNCORRECTED_BLOCKS: 
	{
		u32 *ublocks=(u32 *) arg;
		cx24_writereg(i2c,0x10,0xb0); /* select reading of RS Byte Error Count */
		*ublocks = cx24_readreg(i2c,0x12);
                *ublocks|=(cx24_readreg(i2c,0x13)<<8);
                *ublocks|=(cx24_readreg(i2c,0x14)<<16);
		break;
	}

        case FE_SET_FRONTEND:
	{
		struct dvb_frontend_parameters *p = arg;

		pll_set_tv_freq (i2c, cx, p->frequency);
                SetSymbolrate(i2c, cx, p->u.qpsk.symbol_rate);
		
                if(cx->values_changed) {
                   cx24_writereg(i2c,0x04,0x05); /* start Acquisition */
                }
                cx->values_changed=0;
                break;
	}

	case FE_GET_FRONTEND:
		break;

        case FE_SLEEP:
		return 0;

        case FE_INIT:
        {
                cx24_init(i2c, cx);
                cx24_writereg(i2c,0x76,0x50);
                break;
        }

	case FE_RESET:
		return 0;

	case FE_DISEQC_SEND_MASTER_CMD:
		return -EOPNOTSUPP;

	case FE_DISEQC_SEND_BURST:
		return -EOPNOTSUPP;

	case FE_SET_TONE:
	{
	        fe_sec_tone_mode_t tone_mode = (fe_sec_tone_mode_t)arg;
		int oldval=cx24_readreg(i2c,0x76)&0xaf;
		
		if(tone_mode==SEC_TONE_OFF) {
		   cx24_writereg(i2c,0x76,oldval|0x40);
		} else if(tone_mode==SEC_TONE_ON) {
		   cx24_writereg(i2c,0x76,oldval|0x50);
		} else return -EINVAL;
		break;
	}

	case FE_SET_VOLTAGE:
	{
	        fe_sec_voltage_t voltage = (fe_sec_voltage_t)arg;
		int oldval=cx24_readreg(i2c,0x76)&0x3f;
		
		if(voltage==SEC_VOLTAGE_13) {
		   cx24_writereg(i2c,0x76,oldval|0xc0);
		} else if(voltage==SEC_VOLTAGE_18) {
		   cx24_writereg(i2c,0x76,oldval|0x40);
		} else return -EINVAL;
		break;
	}

	default:
		return -EOPNOTSUPP;
        }
        return 0;
} 

static
int cx24_attach (struct dvb_i2c_bus *i2c)
{
	struct cx24110 *cx;

	u8 chipid = cx24_readreg (i2c, 0x00);

	dprintk ("%s\n", __FUNCTION__);

        /* 2 possible chip ids at address 0: 0x5a is the older cx24106,
           0x69 should be the newer cx24110. Is this sufficient so that
           we do not accidentally access a bad chip? */
	if (chipid != 0x5a && chipid != 0x69)
		return -ENODEV;

        printk("cx24110: attaching cx241%s\n", chipid==0x5a?"06":"10");

	cx=kmalloc(sizeof(struct cx24110),GFP_KERNEL);
        if (cx==NULL) {
                return -ENOMEM;
        }

	dvb_register_frontend (cx24_ioctl, i2c, (void*)cx, 
			       &cx24_info);

	return 0;

}


static
void cx24_detach (struct dvb_i2c_bus *i2c)
{
	dvb_unregister_frontend (cx24_ioctl, i2c);
}


static
int __init init_cx24 (void)
{
	return dvb_register_i2c_device (THIS_MODULE,
					cx24_attach, 
					cx24_detach);
	return 0;
}


static 
void __exit exit_cx24 (void)
{
	dvb_unregister_i2c_device (cx24_attach);
	return;
}

module_init(init_cx24);
module_exit(exit_cx24);


MODULE_DESCRIPTION("DVB Frontend driver module for the Conexant cx24108/cx24110 chipset");
MODULE_AUTHOR("Peter Hettkamp");
MODULE_LICENSE("GPL");
MODULE_PARM(debug,"i");

#!/bin/sh
# insmod modules from current directory without having to install them first

sync

case "$1" in
    load)
	echo -n -e "Inserting av7110 modules into kernel"
	insmod i2c-algo-bit
	insmod ./videodev.o
	insmod ./v4l1-compat.o
	insmod ./v4l2-common.o
	insmod ./video-buf.o
	insmod bttv
	insmod ./dvb-core.o
	insmod ./cx24110.o
	insmod ./bt878.o
	insmod ./dvb-bt8xx.o
	echo
	;;
    debug)
	echo -n -e "Inserting av7110 modules (debug) into kernel"
	insmod i2c-algo-bit
	insmod ./videodev.o
	insmod ./v4l1-compat.o
	insmod ./v4l2-common.o
	insmod ./video-buf.o
	insmod bttv
	insmod ./dvb-core.o
	insmod ./cx24110.o debug=1
	insmod ./bt878.o
	insmod ./dvb-bt8xx.o debug=1
	echo
	;;
    unload)
	echo -n -e "Deleting av7110 modules from kernel"
	rmmod  dvb-bt8xx bt878 cx24110 \
		bttv video-buf v4l2-common v4l1-compat dvb-core videodev
	echo
	;;
    reload)
	$0 stop && $0 debug
	;;
    *)
	echo "Usage$0 {load|unload|debug|reload}"
	exit 1
esac

sync

Home | Main Index | Thread Index