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

📄 bcm3510.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) * *  Copyright (C) 2001-5, B2C2 inc. * *  GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@desy.de> * *  This driver is "hard-coded" to be used with the 1st generation of *  Technisat/B2C2's Air2PC ATSC PCI/USB cards/boxes. The pll-programming *  (Panasonic CT10S) is located here, which is actually wrong. Unless there is *  another device with a BCM3510, this is no problem. * *  The driver works also with QAM64 DVB-C, but had an unreasonable high *  UNC. (Tested with the Air2PC ATSC 1st generation) * *  You'll need a firmware for this driver in order to get it running. It is *  called "dvb-fe-bcm3510-01.fw". * * 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/init.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/device.h>#include <linux/firmware.h>#include <linux/jiffies.h>#include <linux/string.h>#include <linux/slab.h>#include "dvb_frontend.h"#include "bcm3510.h"#include "bcm3510_priv.h"struct bcm3510_state {	struct i2c_adapter* i2c;	struct dvb_frontend_ops ops;	const struct bcm3510_config* config;	struct dvb_frontend frontend;	/* demodulator private data */	struct semaphore hab_sem;	u8 firmware_loaded:1;	unsigned long next_status_check;	unsigned long status_check_interval;	struct bcm3510_hab_cmd_status1 status1;	struct bcm3510_hab_cmd_status2 status2;};static int debug;module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c (|-able)).");#define dprintk(level,x...) if (level & debug) printk(x)#define dbufout(b,l,m) {\	    int i; \	    for (i = 0; i < l; i++) \		m("%02x ",b[i]); \}#define deb_info(args...) dprintk(0x01,args)#define deb_i2c(args...)  dprintk(0x02,args)#define deb_hab(args...)  dprintk(0x04,args)/* transfer functions */static int bcm3510_writebytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len){	u8 b[256];	int err;	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = len + 1 };	b[0] = reg;	memcpy(&b[1],buf,len);	deb_i2c("i2c wr %02x: ",reg);	dbufout(buf,len,deb_i2c);	deb_i2c("\n");	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {		deb_info("%s: i2c write error (addr %02x, reg %02x, err == %i)\n",			__FUNCTION__, state->config->demod_address, reg,  err);		return -EREMOTEIO;	}	return 0;}static int bcm3510_readbytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len){	struct i2c_msg msg[] = {		{ .addr = state->config->demod_address, .flags = 0,        .buf = &reg, .len = 1 },		{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf,  .len = len }	};	int err;	memset(buf,0,len);	if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) {		deb_info("%s: i2c read error (addr %02x, reg %02x, err == %i)\n",			__FUNCTION__, state->config->demod_address, reg,  err);		return -EREMOTEIO;	}	deb_i2c("i2c rd %02x: ",reg);	dbufout(buf,len,deb_i2c);	deb_i2c("\n");	return 0;}static int bcm3510_writeB(struct bcm3510_state *state, u8 reg, bcm3510_register_value v){	return bcm3510_writebytes(state,reg,&v.raw,1);}static int bcm3510_readB(struct bcm3510_state *state, u8 reg, bcm3510_register_value *v){	return bcm3510_readbytes(state,reg,&v->raw,1);}/* Host Access Buffer transfers */static int bcm3510_hab_get_response(struct bcm3510_state *st, u8 *buf, int len){	bcm3510_register_value v;	int ret,i;	v.HABADR_a6.HABADR = 0;	if ((ret = bcm3510_writeB(st,0xa6,v)) < 0)		return ret;	for (i = 0; i < len; i++) {		if ((ret = bcm3510_readB(st,0xa7,&v)) < 0)			return ret;		buf[i] = v.HABDATA_a7;	}	return 0;}static int bcm3510_hab_send_request(struct bcm3510_state *st, u8 *buf, int len){	bcm3510_register_value v,hab;	int ret,i;	unsigned long t;/* Check if any previous HAB request still needs to be serviced by the * Aquisition Processor before sending new request */	if ((ret = bcm3510_readB(st,0xa8,&v)) < 0)		return ret;	if (v.HABSTAT_a8.HABR) {		deb_info("HAB is running already - clearing it.\n");		v.HABSTAT_a8.HABR = 0;		bcm3510_writeB(st,0xa8,v);//		return -EBUSY;	}/* Send the start HAB Address (automatically incremented after write of * HABDATA) and write the HAB Data */	hab.HABADR_a6.HABADR = 0;	if ((ret = bcm3510_writeB(st,0xa6,hab)) < 0)		return ret;	for (i = 0; i < len; i++) {		hab.HABDATA_a7 = buf[i];		if ((ret = bcm3510_writeB(st,0xa7,hab)) < 0)			return ret;	}/* Set the HABR bit to indicate AP request in progress (LBHABR allows HABR to * be written) */	v.raw = 0; v.HABSTAT_a8.HABR = 1; v.HABSTAT_a8.LDHABR = 1;	if ((ret = bcm3510_writeB(st,0xa8,v)) < 0)		return ret;/* Polling method: Wait until the AP finishes processing the HAB request */	t = jiffies + 1*HZ;	while (time_before(jiffies, t)) {		deb_info("waiting for HAB to complete\n");		msleep(10);		if ((ret = bcm3510_readB(st,0xa8,&v)) < 0)			return ret;		if (!v.HABSTAT_a8.HABR)			return 0;	}	deb_info("send_request execution timed out.\n");	return -ETIMEDOUT;}static int bcm3510_do_hab_cmd(struct bcm3510_state *st, u8 cmd, u8 msgid, u8 *obuf, u8 olen, u8 *ibuf, u8 ilen){	u8 ob[olen+2],ib[ilen+2];	int ret = 0;	ob[0] = cmd;	ob[1] = msgid;	memcpy(&ob[2],obuf,olen);	deb_hab("hab snd: ");	dbufout(ob,olen+2,deb_hab);	deb_hab("\n");	if (down_interruptible(&st->hab_sem) < 0)		return -EAGAIN;	if ((ret = bcm3510_hab_send_request(st, ob, olen+2)) < 0 ||		(ret = bcm3510_hab_get_response(st, ib, ilen+2)) < 0)		goto error;	deb_hab("hab get: ");	dbufout(ib,ilen+2,deb_hab);	deb_hab("\n");	memcpy(ibuf,&ib[2],ilen);error:	up(&st->hab_sem);	return ret;}#if 0/* not needed, we use a semaphore to prevent HAB races */static int bcm3510_is_ap_ready(struct bcm3510_state *st){	bcm3510_register_value ap,hab;	int ret;	if ((ret = bcm3510_readB(st,0xa8,&hab)) < 0 ||		(ret = bcm3510_readB(st,0xa2,&ap) < 0))		return ret;	if (ap.APSTAT1_a2.RESET || ap.APSTAT1_a2.IDLE || ap.APSTAT1_a2.STOP || hab.HABSTAT_a8.HABR) {		deb_info("AP is busy\n");		return -EBUSY;	}	return 0;}#endifstatic int bcm3510_bert_reset(struct bcm3510_state *st){	bcm3510_register_value b;	int ret;	if ((ret < bcm3510_readB(st,0xfa,&b)) < 0)		return ret;	b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b);	b.BERCTL_fa.RESYNC = 1; bcm3510_writeB(st,0xfa,b);	b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b);	b.BERCTL_fa.CNTCTL = 1; b.BERCTL_fa.BITCNT = 1; bcm3510_writeB(st,0xfa,b);	/* clear residual bit counter TODO  */	return 0;}static int bcm3510_refresh_state(struct bcm3510_state *st){	if (time_after(jiffies,st->next_status_check)) {		bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS1, NULL,0, (u8 *)&st->status1, sizeof(st->status1));		bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS2, NULL,0, (u8 *)&st->status2, sizeof(st->status2));		st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000;	}	return 0;}static int bcm3510_read_status(struct dvb_frontend *fe, fe_status_t *status){	struct bcm3510_state* st = fe->demodulator_priv;	bcm3510_refresh_state(st);	*status = 0;	if (st->status1.STATUS1.RECEIVER_LOCK)		*status |= FE_HAS_LOCK | FE_HAS_SYNC;	if (st->status1.STATUS1.FEC_LOCK)		*status |= FE_HAS_VITERBI;	if (st->status1.STATUS1.OUT_PLL_LOCK)		*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;	if (*status & FE_HAS_LOCK)		st->status_check_interval = 1500;	else /* more frequently checks if no lock has been achieved yet */		st->status_check_interval = 500;	deb_info("real_status: %02x\n",*status);	return 0;}static int bcm3510_read_ber(struct dvb_frontend* fe, u32* ber){	struct bcm3510_state* st = fe->demodulator_priv;	bcm3510_refresh_state(st);	*ber = (st->status2.LDBER0 << 16) | (st->status2.LDBER1 << 8) | st->status2.LDBER2;	return 0;}static int bcm3510_read_unc(struct dvb_frontend* fe, u32* unc){	struct bcm3510_state* st = fe->demodulator_priv;	bcm3510_refresh_state(st);	*unc = (st->status2.LDUERC0 << 8) | st->status2.LDUERC1;	return 0;}static int bcm3510_read_signal_strength(struct dvb_frontend* fe, u16* strength){	struct bcm3510_state* st = fe->demodulator_priv;	s32 t;	bcm3510_refresh_state(st);	t = st->status2.SIGNAL;	if (t > 190)		t = 190;	if (t < 90)		t = 90;	t -= 90;	t = t * 0xff / 100;	/* normalize if necessary */	*strength = (t << 8) | t;	return 0;}static int bcm3510_read_snr(struct dvb_frontend* fe, u16* snr){	struct bcm3510_state* st = fe->demodulator_priv;	bcm3510_refresh_state(st);	*snr = st->status1.SNR_EST0*1000 + ((st->status1.SNR_EST1*1000) >> 8);	return 0;}/* tuner frontend programming */static int bcm3510_tuner_cmd(struct bcm3510_state* st,u8 bc, u16 n, u8 a){	struct bcm3510_hab_cmd_tune c;	memset(&c,0,sizeof(struct bcm3510_hab_cmd_tune));/* I2C Mode disabled,  set 16 control / Data pairs */	c.length = 0x10;	c.clock_width = 0;/* CS1, CS0, DATA, CLK bits control the tuner RF_AGC_SEL pin is set to * logic high (as Configuration) */	c.misc = 0x10;/* Set duration of the initial state of TUNCTL = 3.34 micro Sec */	c.TUNCTL_state = 0x40;/* PRESCALER DEVIDE RATIO | BC1_2_3_4; (band switch), 1stosc REFERENCE COUNTER REF_S12 and REF_S11 */	c.ctl_dat[0].ctrl.size = BITS_8;	c.ctl_dat[0].data      = 0x80 | bc;/* Control DATA pin, 1stosc REFERENCE COUNTER REF_S10 to REF_S3 */	c.ctl_dat[1].ctrl.size = BITS_8;	c.ctl_dat[1].data      = 4;/* set CONTROL BIT 1 to 1, 1stosc REFERENCE COUNTER REF_S2 to REF_S1 */	c.ctl_dat[2].ctrl.size = BITS_3;	c.ctl_dat[2].data      = 0x20;/* control CS0 pin, pulse byte ? */	c.ctl_dat[3].ctrl.size = BITS_3;	c.ctl_dat[3].ctrl.clk_off = 1;	c.ctl_dat[3].ctrl.cs0  = 1;	c.ctl_dat[3].data      = 0x40;/* PGM_S18 to PGM_S11 */	c.ctl_dat[4].ctrl.size = BITS_8;	c.ctl_dat[4].data      = n >> 3;/* PGM_S10 to PGM_S8, SWL_S7 to SWL_S3 */	c.ctl_dat[5].ctrl.size = BITS_8;	c.ctl_dat[5].data      = ((n & 0x7) << 5) | (a >> 2);/* SWL_S2 and SWL_S1, set CONTROL BIT 2 to 0 */	c.ctl_dat[6].ctrl.size = BITS_3;	c.ctl_dat[6].data      = (a << 6) & 0xdf;/* control CS0 pin, pulse byte ? */	c.ctl_dat[7].ctrl.size = BITS_3;	c.ctl_dat[7].ctrl.clk_off = 1;	c.ctl_dat[7].ctrl.cs0  = 1;	c.ctl_dat[7].data      = 0x40;/* PRESCALER DEVIDE RATIO, 2ndosc REFERENCE COUNTER REF_S12 and REF_S11 */	c.ctl_dat[8].ctrl.size = BITS_8;	c.ctl_dat[8].data      = 0x80;/* 2ndosc REFERENCE COUNTER REF_S10 to REF_S3 */	c.ctl_dat[9].ctrl.size = BITS_8;	c.ctl_dat[9].data      = 0x10;/* set CONTROL BIT 1 to 1, 2ndosc REFERENCE COUNTER REF_S2 to REF_S1 */	c.ctl_dat[10].ctrl.size = BITS_3;	c.ctl_dat[10].data      = 0x20;/* pulse byte */	c.ctl_dat[11].ctrl.size = BITS_3;	c.ctl_dat[11].ctrl.clk_off = 1;	c.ctl_dat[11].ctrl.cs1  = 1;	c.ctl_dat[11].data      = 0x40;/* PGM_S18 to PGM_S11 */	c.ctl_dat[12].ctrl.size = BITS_8;	c.ctl_dat[12].data      = 0x2a;/* PGM_S10 to PGM_S8 and SWL_S7 to SWL_S3 */	c.ctl_dat[13].ctrl.size = BITS_8;	c.ctl_dat[13].data      = 0x8e;/* SWL_S2 and SWL_S1 and set CONTROL BIT 2 to 0 */	c.ctl_dat[14].ctrl.size = BITS_3;	c.ctl_dat[14].data      = 0;

⌨️ 快捷键说明

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