⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cx22702.c

📁 LINUX 2.6.17.4的源码
💻 C
字号:
/*    Conexant 22702 DVB OFDM demodulator driver    based on:	Alps TDMB7 DVB OFDM demodulator driver    Copyright (C) 2001-2002 Convergence Integrated Media GmbH	  Holger Waechtler <holger@convergence.de>    Copyright (C) 2004 Steven Toth <stoth@hauppauge.com>    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.*/#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/delay.h>#include "dvb_frontend.h"#include "dvb-pll.h"#include "cx22702.h"struct cx22702_state {	struct i2c_adapter* i2c;	struct dvb_frontend_ops ops;	/* configuration settings */	const struct cx22702_config* config;	struct dvb_frontend frontend;	/* previous uncorrected block counter */	u8 prevUCBlocks;};static int debug = 0;#define dprintk	if (debug) printk/* Register values to initialise the demod */static u8 init_tab [] = {	0x00, 0x00, /* Stop aquisition */	0x0B, 0x06,	0x09, 0x01,	0x0D, 0x41,	0x16, 0x32,	0x20, 0x0A,	0x21, 0x17,	0x24, 0x3e,	0x26, 0xff,	0x27, 0x10,	0x28, 0x00,	0x29, 0x00,	0x2a, 0x10,	0x2b, 0x00,	0x2c, 0x10,	0x2d, 0x00,	0x48, 0xd4,	0x49, 0x56,	0x6b, 0x1e,	0xc8, 0x02,	0xf9, 0x00,	0xfa, 0x00,	0xfb, 0x00,	0xfc, 0x00,	0xfd, 0x00,};static int cx22702_writereg (struct cx22702_state* state, u8 reg, u8 data){	int ret;	u8 buf [] = { reg, data };	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };	ret = i2c_transfer(state->i2c, &msg, 1);	if (ret != 1)		printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",			__FUNCTION__, reg, data, ret);	return (ret != 1) ? -1 : 0;}static u8 cx22702_readreg (struct cx22702_state* state, u8 reg){	int ret;	u8 b0 [] = { reg };	u8 b1 [] = { 0 };	struct i2c_msg msg [] = {		{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },		{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };	ret = i2c_transfer(state->i2c, msg, 2);	if (ret != 2)		printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);	return b1[0];}static int cx22702_set_inversion (struct cx22702_state *state, int inversion){	u8 val;	switch (inversion) {		case INVERSION_AUTO:			return -EOPNOTSUPP;		case INVERSION_ON:			val = cx22702_readreg (state, 0x0C);			return cx22702_writereg (state, 0x0C, val | 0x01);		case INVERSION_OFF:			val = cx22702_readreg (state, 0x0C);			return cx22702_writereg (state, 0x0C, val & 0xfe);		default:			return -EINVAL;	}}/* Retrieve the demod settings */static int cx22702_get_tps (struct cx22702_state *state, struct dvb_ofdm_parameters *p){	u8 val;	/* Make sure the TPS regs are valid */	if (!(cx22702_readreg(state, 0x0A) & 0x20))		return -EAGAIN;	val = cx22702_readreg (state, 0x01);	switch( (val&0x18)>>3) {		case 0: p->constellation =   QPSK; break;		case 1: p->constellation = QAM_16; break;		case 2: p->constellation = QAM_64; break;	}	switch( val&0x07 ) {		case 0: p->hierarchy_information = HIERARCHY_NONE; break;		case 1: p->hierarchy_information =    HIERARCHY_1; break;		case 2: p->hierarchy_information =    HIERARCHY_2; break;		case 3: p->hierarchy_information =    HIERARCHY_4; break;	}	val = cx22702_readreg (state, 0x02);	switch( (val&0x38)>>3 ) {		case 0: p->code_rate_HP = FEC_1_2; break;		case 1: p->code_rate_HP = FEC_2_3; break;		case 2: p->code_rate_HP = FEC_3_4; break;		case 3: p->code_rate_HP = FEC_5_6; break;		case 4: p->code_rate_HP = FEC_7_8; break;	}	switch( val&0x07 ) {		case 0: p->code_rate_LP = FEC_1_2; break;		case 1: p->code_rate_LP = FEC_2_3; break;		case 2: p->code_rate_LP = FEC_3_4; break;		case 3: p->code_rate_LP = FEC_5_6; break;		case 4: p->code_rate_LP = FEC_7_8; break;	}	val = cx22702_readreg (state, 0x03);	switch( (val&0x0c)>>2 ) {		case 0: p->guard_interval = GUARD_INTERVAL_1_32; break;		case 1: p->guard_interval = GUARD_INTERVAL_1_16; break;		case 2: p->guard_interval =  GUARD_INTERVAL_1_8; break;		case 3: p->guard_interval =  GUARD_INTERVAL_1_4; break;	}	switch( val&0x03 ) {		case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break;		case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break;	}	return 0;}static int cx22702_i2c_gate_ctrl(struct dvb_frontend* fe, int enable){	struct cx22702_state* state = fe->demodulator_priv;	dprintk ("%s(%d)\n", __FUNCTION__, enable);	if (enable)		return cx22702_writereg (state, 0x0D, cx22702_readreg(state, 0x0D) & 0xfe);	else		return cx22702_writereg (state, 0x0D, cx22702_readreg(state, 0x0D) | 1);}/* Talk to the demod, set the FEC, GUARD, QAM settings etc */static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_parameters *p){	u8 val;	struct cx22702_state* state = fe->demodulator_priv;	/* set PLL */	cx22702_i2c_gate_ctrl(fe, 1);	if (state->config->pll_set) {		state->config->pll_set(fe, p);	} else if (state->config->pll_desc) {		u8 pllbuf[4];		struct i2c_msg msg = { .addr = state->config->pll_address,				       .buf = pllbuf, .len = 4 };		dvb_pll_configure(state->config->pll_desc, pllbuf,				  p->frequency,				  p->u.ofdm.bandwidth);		i2c_transfer(state->i2c, &msg, 1);	} else {		BUG();	}	cx22702_i2c_gate_ctrl(fe, 0);	/* set inversion */	cx22702_set_inversion (state, p->inversion);	/* set bandwidth */	switch(p->u.ofdm.bandwidth) {	case BANDWIDTH_6_MHZ:		cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x20 );		break;	case BANDWIDTH_7_MHZ:		cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x10 );		break;	case BANDWIDTH_8_MHZ:		cx22702_writereg(state, 0x0C, cx22702_readreg(state, 0x0C) &0xcf );		break;	default:		dprintk ("%s: invalid bandwidth\n",__FUNCTION__);		return -EINVAL;	}	p->u.ofdm.code_rate_LP = FEC_AUTO; //temp hack as manual not working	/* use auto configuration? */	if((p->u.ofdm.hierarchy_information==HIERARCHY_AUTO) ||	   (p->u.ofdm.constellation==QAM_AUTO) ||	   (p->u.ofdm.code_rate_HP==FEC_AUTO) ||	   (p->u.ofdm.code_rate_LP==FEC_AUTO) ||	   (p->u.ofdm.guard_interval==GUARD_INTERVAL_AUTO) ||	   (p->u.ofdm.transmission_mode==TRANSMISSION_MODE_AUTO) ) {		/* TPS Source - use hardware driven values */		cx22702_writereg(state, 0x06, 0x10);		cx22702_writereg(state, 0x07, 0x9);		cx22702_writereg(state, 0x08, 0xC1);		cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) & 0xfc );		cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );		cx22702_writereg(state, 0x00, 0x01); /* Begin aquisition */		dprintk("%s: Autodetecting\n",__FUNCTION__);		return 0;	}	/* manually programmed values */	val=0;	switch(p->u.ofdm.constellation) {		case   QPSK: val = (val&0xe7); break;		case QAM_16: val = (val&0xe7)|0x08; break;		case QAM_64: val = (val&0xe7)|0x10; break;		default:			dprintk ("%s: invalid constellation\n",__FUNCTION__);			return -EINVAL;	}	switch(p->u.ofdm.hierarchy_information) {		case HIERARCHY_NONE: val = (val&0xf8); break;		case    HIERARCHY_1: val = (val&0xf8)|1; break;		case    HIERARCHY_2: val = (val&0xf8)|2; break;		case    HIERARCHY_4: val = (val&0xf8)|3; break;		default:			dprintk ("%s: invalid hierarchy\n",__FUNCTION__);			return -EINVAL;	}	cx22702_writereg (state, 0x06, val);	val=0;	switch(p->u.ofdm.code_rate_HP) {		case FEC_NONE:		case FEC_1_2: val = (val&0xc7); break;		case FEC_2_3: val = (val&0xc7)|0x08; break;		case FEC_3_4: val = (val&0xc7)|0x10; break;		case FEC_5_6: val = (val&0xc7)|0x18; break;		case FEC_7_8: val = (val&0xc7)|0x20; break;		default:			dprintk ("%s: invalid code_rate_HP\n",__FUNCTION__);			return -EINVAL;	}	switch(p->u.ofdm.code_rate_LP) {		case FEC_NONE:		case FEC_1_2: val = (val&0xf8); break;		case FEC_2_3: val = (val&0xf8)|1; break;		case FEC_3_4: val = (val&0xf8)|2; break;		case FEC_5_6: val = (val&0xf8)|3; break;		case FEC_7_8: val = (val&0xf8)|4; break;		default:			dprintk ("%s: invalid code_rate_LP\n",__FUNCTION__);			return -EINVAL;	}	cx22702_writereg (state, 0x07, val);	val=0;	switch(p->u.ofdm.guard_interval) {		case GUARD_INTERVAL_1_32: val = (val&0xf3); break;		case GUARD_INTERVAL_1_16: val = (val&0xf3)|0x04; break;		case  GUARD_INTERVAL_1_8: val = (val&0xf3)|0x08; break;		case  GUARD_INTERVAL_1_4: val = (val&0xf3)|0x0c; break;		default:			dprintk ("%s: invalid guard_interval\n",__FUNCTION__);			return -EINVAL;	}	switch(p->u.ofdm.transmission_mode) {		case TRANSMISSION_MODE_2K: val = (val&0xfc); break;		case TRANSMISSION_MODE_8K: val = (val&0xfc)|1; break;		default:			dprintk ("%s: invalid transmission_mode\n",__FUNCTION__);			return -EINVAL;	}	cx22702_writereg(state, 0x08, val);	cx22702_writereg(state, 0x0B, (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02 );	cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );	/* Begin channel aquisition */	cx22702_writereg(state, 0x00, 0x01);	return 0;}/* Reset the demod hardware and reset all of the configuration registers   to a default state. */static int cx22702_init (struct dvb_frontend* fe){	int i;	struct cx22702_state* state = fe->demodulator_priv;	cx22702_writereg (state, 0x00, 0x02);	msleep(10);	for (i=0; i<sizeof(init_tab); i+=2)		cx22702_writereg (state, init_tab[i], init_tab[i+1]);	cx22702_writereg (state, 0xf8, (state->config->output_mode << 1) & 0x02);	/* init PLL */	if (state->config->pll_init)		state->config->pll_init(fe);	cx22702_i2c_gate_ctrl(fe, 0);	return 0;}static int cx22702_read_status(struct dvb_frontend* fe, fe_status_t* status){	struct cx22702_state* state = fe->demodulator_priv;	u8 reg0A;	u8 reg23;	*status = 0;	reg0A = cx22702_readreg (state, 0x0A);	reg23 = cx22702_readreg (state, 0x23);	dprintk ("%s: status demod=0x%02x agc=0x%02x\n"		,__FUNCTION__,reg0A,reg23);	if(reg0A & 0x10) {		*status |= FE_HAS_LOCK;		*status |= FE_HAS_VITERBI;		*status |= FE_HAS_SYNC;	}	if(reg0A & 0x20)		*status |= FE_HAS_CARRIER;	if(reg23 < 0xf0)		*status |= FE_HAS_SIGNAL;	return 0;}static int cx22702_read_ber(struct dvb_frontend* fe, u32* ber){	struct cx22702_state* state = fe->demodulator_priv;	if(cx22702_readreg (state, 0xE4) & 0x02) {		/* Realtime statistics */		*ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7			| (cx22702_readreg (state, 0xDF)&0x7F);	} else {		/* Averagtine statistics */		*ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7			| cx22702_readreg (state, 0xDF);	}	return 0;}static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength){	struct cx22702_state* state = fe->demodulator_priv;	*signal_strength = cx22702_readreg (state, 0x23);	return 0;}static int cx22702_read_snr(struct dvb_frontend* fe, u16* snr){	struct cx22702_state* state = fe->demodulator_priv;	u16 rs_ber=0;	if(cx22702_readreg (state, 0xE4) & 0x02) {		/* Realtime statistics */		rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7			| (cx22702_readreg (state, 0xDF)& 0x7F);	} else {		/* Averagine statistics */		rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 8			| cx22702_readreg (state, 0xDF);	}	*snr = ~rs_ber;	return 0;}static int cx22702_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks){	struct cx22702_state* state = fe->demodulator_priv;	u8 _ucblocks;	/* RS Uncorrectable Packet Count then reset */	_ucblocks = cx22702_readreg (state, 0xE3);	if (state->prevUCBlocks < _ucblocks)		*ucblocks = (_ucblocks - state->prevUCBlocks);	else		*ucblocks = state->prevUCBlocks - _ucblocks;	state->prevUCBlocks = _ucblocks;	return 0;}static int cx22702_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p){	struct cx22702_state* state = fe->demodulator_priv;	u8 reg0C = cx22702_readreg (state, 0x0C);	p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF;	return cx22702_get_tps (state, &p->u.ofdm);}static int cx22702_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune){	tune->min_delay_ms = 1000;	return 0;}static void cx22702_release(struct dvb_frontend* fe){	struct cx22702_state* state = fe->demodulator_priv;	kfree(state);}static struct dvb_frontend_ops cx22702_ops;struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,				    struct i2c_adapter* i2c){	struct cx22702_state* state = NULL;	/* allocate memory for the internal state */	state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);	if (state == NULL)		goto error;	/* setup the state */	state->config = config;	state->i2c = i2c;	memcpy(&state->ops, &cx22702_ops, sizeof(struct dvb_frontend_ops));	state->prevUCBlocks = 0;	/* check if the demod is there */	if (cx22702_readreg(state, 0x1f) != 0x3)		goto error;	/* create dvb_frontend */	state->frontend.ops = &state->ops;	state->frontend.demodulator_priv = state;	return &state->frontend;error:	kfree(state);	return NULL;}static struct dvb_frontend_ops cx22702_ops = {	.info = {		.name			= "Conexant CX22702 DVB-T",		.type			= FE_OFDM,		.frequency_min		= 177000000,		.frequency_max		= 858000000,		.frequency_stepsize	= 166666,		.caps = 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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |		FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |		FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER	},	.release = cx22702_release,	.init = cx22702_init,	.set_frontend = cx22702_set_tps,	.get_frontend = cx22702_get_frontend,	.get_tune_settings = cx22702_get_tune_settings,	.read_status = cx22702_read_status,	.read_ber = cx22702_read_ber,	.read_signal_strength = cx22702_read_signal_strength,	.read_snr = cx22702_read_snr,	.read_ucblocks = cx22702_read_ucblocks,	.i2c_gate_ctrl = cx22702_i2c_gate_ctrl,};module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "Enable verbose debug messages");MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver");MODULE_AUTHOR("Steven Toth");MODULE_LICENSE("GPL");EXPORT_SYMBOL(cx22702_attach);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -