📄 cx24110.c
字号:
/* set AcqVitDis bit */ if(rate[fec]>0) { cx24110_writereg(i2c,0x05,(cx24110_readreg(i2c,0x05)&0xf0)|rate[fec]); /* set nominal Viterbi rate */ cx24110_writereg(i2c,0x22,(cx24110_readreg(i2c,0x22)&0xf0)|rate[fec]); /* set current Viterbi rate */ cx24110_writereg(i2c,0x1a,g1[fec]); cx24110_writereg(i2c,0x1b,g2[fec]); /* not sure if this is the right way: I always used AutoAcq mode */ } else return -EOPNOTSUPP;/* fixme (low): which is the correct return code? */ }; return 0;}static fe_code_rate_t cx24110_get_fec (struct dvb_i2c_bus *i2c){ int i; i=cx24110_readreg(i2c,0x22)&0x0f; if(!(i&0x08)) { return FEC_1_2 + i - 1; } else {/* fixme (low): a special code rate has been selected. In theory, we need to return a denominator value, a numerator value, and a pair of puncture maps to correctly describe this mode. But this should never happen in practice, because it cannot be set by cx24110_get_fec. */ return FEC_NONE; }}static int cx24110_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate){/* fixme (low): add error handling */ u32 ratio; u32 tmp, fclk, BDRI; static const u32 bands[]={5000000UL,15000000UL,90999000UL/2}; static const u32 vca[]={0x80f03800,0x81f0f800,0x83f1f800}; static const u32 vga[]={0x5f8fc000,0x580f0000,0x500c0000}; static const u8 filtune[]={0xa2,0xcc,0x66}; int i;dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate); if (srate>90999000UL/2) srate=90999000UL/2; if (srate<500000) srate=500000; 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=cx24110_readreg(i2c,0x07)&0xfc; if(srate<90999000UL/4) { /* sample rate 45MHz*/ cx24110_writereg(i2c,0x07,tmp); cx24110_writereg(i2c,0x06,0x78); fclk=90999000UL/2; } else if(srate<60666000UL/2) { /* sample rate 60MHz */ cx24110_writereg(i2c,0x07,tmp|0x1); cx24110_writereg(i2c,0x06,0xa5); fclk=60666000UL; } else if(srate<80888000UL/2) { /* sample rate 80MHz */ cx24110_writereg(i2c,0x07,tmp|0x2); cx24110_writereg(i2c,0x06,0x87); fclk=80888000UL; } else { /* sample rate 90MHz */ cx24110_writereg(i2c,0x07,tmp|0x3); cx24110_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 complex 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); cx24110_writereg(i2c, 0x1, (ratio>>16)&0xff); cx24110_writereg(i2c, 0x2, (ratio>>8)&0xff); cx24110_writereg(i2c, 0x3, (ratio)&0xff); /* please see the cx24108 data sheet, this controls tuner gain and bandwidth settings depending on the symbol rate */ cx24108_write(i2c,vga[i]); cx24108_write(i2c,vca[i]); /* gain is set on tuner chip */ cx24110_writereg(i2c,0x56,filtune[i]); /* bw is contolled by filtune voltage */ return 0;}static int cx24110_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage){ switch (voltage) { case SEC_VOLTAGE_13: return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&0x3b)|0xc0); case SEC_VOLTAGE_18: return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&0x3b)|0x40); default: return -EINVAL; };}static void sendDiSEqCMessage(struct dvb_i2c_bus *i2c, struct dvb_diseqc_master_cmd *pCmd){ int i, rv; for (i = 0; i < pCmd->msg_len; i++) cx24110_writereg(i2c, 0x79 + i, pCmd->msg[i]); rv = cx24110_readreg(i2c, 0x76); cx24110_writereg(i2c, 0x76, ((rv & 0x90) | 0x40) | ((pCmd->msg_len-3) & 3)); for (i=500; i-- > 0 && !(cx24110_readreg(i2c,0x76)&0x40);) ; /* wait for LNB ready */}static int cx24110_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg){ struct dvb_i2c_bus *i2c = fe->i2c; static int lastber=0, lastbyer=0,lastbler=0, lastesn0=0, sum_bler=0; switch (cmd) { case FE_GET_INFO: memcpy (arg, &cx24110_info, sizeof(struct dvb_frontend_info)); break; case FE_READ_STATUS: { fe_status_t *status = arg; int sync = cx24110_readreg (i2c, 0x55); *status = 0; if (sync & 0x10) *status |= FE_HAS_SIGNAL; if (sync & 0x08) *status |= FE_HAS_CARRIER; sync = cx24110_readreg (i2c, 0x08); if (sync & 0x40) *status |= FE_HAS_VITERBI; if (sync & 0x20) *status |= FE_HAS_SYNC; if ((sync & 0x60) == 0x60) *status |= FE_HAS_LOCK; if(cx24110_readreg(i2c,0x10)&0x40) { /* the RS error counter has finished one counting window */ cx24110_writereg(i2c,0x10,0x60); /* select the byer reg */ lastbyer=cx24110_readreg(i2c,0x12)| (cx24110_readreg(i2c,0x13)<<8)| (cx24110_readreg(i2c,0x14)<<16); cx24110_writereg(i2c,0x10,0x70); /* select the bler reg */ lastbler=cx24110_readreg(i2c,0x12)| (cx24110_readreg(i2c,0x13)<<8)| (cx24110_readreg(i2c,0x14)<<16); cx24110_writereg(i2c,0x10,0x20); /* start new count window */ sum_bler += lastbler; } if(cx24110_readreg(i2c,0x24)&0x10) { /* the Viterbi error counter has finished one counting window */ cx24110_writereg(i2c,0x24,0x04); /* select the ber reg */ lastber=cx24110_readreg(i2c,0x25)| (cx24110_readreg(i2c,0x26)<<8); cx24110_writereg(i2c,0x24,0x04); /* start new count window */ cx24110_writereg(i2c,0x24,0x14); } if(cx24110_readreg(i2c,0x6a)&0x80) { /* the Es/N0 error counter has finished one counting window */ lastesn0=cx24110_readreg(i2c,0x69)| (cx24110_readreg(i2c,0x68)<<8); cx24110_writereg(i2c,0x6a,0x84); /* start new count window */ } break; } case FE_READ_BER: { u32 *ber = (u32 *) arg; *ber = lastber;/* fixme (maybe): value range is 16 bit. Scale? */ break; } case FE_READ_SIGNAL_STRENGTH: {/* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */ u8 signal = cx24110_readreg (i2c, 0x27)+128; *((u16*) arg) = (signal << 8) | signal; break; } case FE_READ_SNR: {/* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */ *(u16*) arg = lastesn0; break; } case FE_READ_UNCORRECTED_BLOCKS: { *(u16*) arg = sum_bler&0xffff; sum_bler=0; break; } case FE_SET_FRONTEND: { struct dvb_frontend_parameters *p = arg; cx24108_set_tv_freq (i2c, p->frequency); cx24110_set_inversion (i2c, p->inversion); cx24110_set_fec (i2c, p->u.qpsk.fec_inner); cx24110_set_symbolrate (i2c, p->u.qpsk.symbol_rate); cx24110_writereg(i2c,0x04,0x05); /* start aquisition */ break; } case FE_GET_FRONTEND: { struct dvb_frontend_parameters *p = arg; s32 afc; unsigned sclk;/* cannot read back tuner settings (freq). Need to have some private storage */ sclk = cx24110_readreg (i2c, 0x07) & 0x03;/* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz. * Need 64 bit arithmetic. Is thiss possible in the kernel? */ if (sclk==0) sclk=90999000L/2L; else if (sclk==1) sclk=60666000L; else if (sclk==2) sclk=80888000L; else sclk=90999000L; sclk>>=8; afc = sclk*(cx24110_readreg (i2c, 0x44)&0x1f)+ ((sclk*cx24110_readreg (i2c, 0x45))>>8)+ ((sclk*cx24110_readreg (i2c, 0x46))>>16); p->frequency += afc; p->inversion = (cx24110_readreg (i2c, 0x22) & 0x10) ? INVERSION_ON : INVERSION_OFF; p->u.qpsk.fec_inner = cx24110_get_fec (i2c); break; } case FE_SLEEP:/* cannot do this from the FE end. How to communicate this to the place where it can be done? */ break; case FE_INIT: return cx24110_init (i2c); case FE_SET_TONE: return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&~0x10)|((((fe_sec_tone_mode_t) arg)==SEC_TONE_ON)?0x10:0)); case FE_SET_VOLTAGE: return cx24110_set_voltage (i2c, (fe_sec_voltage_t) arg); case FE_DISEQC_SEND_MASTER_CMD: sendDiSEqCMessage(i2c, (struct dvb_diseqc_master_cmd*) arg); return 0; default: return -EOPNOTSUPP; }; return 0;}static int cx24110_attach (struct dvb_i2c_bus *i2c, void **data){ u8 sig; sig=cx24110_readreg (i2c, 0x00); if ( sig != 0x5a && sig != 0x69 ) return -ENODEV; return dvb_register_frontend (cx24110_ioctl, i2c, NULL, &cx24110_info);}static void cx24110_detach (struct dvb_i2c_bus *i2c, void *data){ dvb_unregister_frontend (cx24110_ioctl, i2c);}static int __init init_cx24110 (void){ return dvb_register_i2c_device (THIS_MODULE, cx24110_attach, cx24110_detach);}static void __exit exit_cx24110 (void){ dvb_unregister_i2c_device (cx24110_attach);}module_init(init_cx24110);module_exit(exit_cx24110);MODULE_DESCRIPTION("DVB Frontend driver module for the Conexant cx24108/cx24110 chipset");MODULE_AUTHOR("Peter Hettkamp");MODULE_LICENSE("GPL");MODULE_PARM(debug,"i");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -