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

📄 tda1004x.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
  /*     Driver for Philips tda1004xh OFDM Demodulator     (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach     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.   *//* * This driver needs external firmware. Please use the commands * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045", * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to * download/extract them, and then copy them to /usr/lib/hotplug/firmware. */#define TDA10045_DEFAULT_FIRMWARE "dvb-fe-tda10045.fw"#define TDA10046_DEFAULT_FIRMWARE "dvb-fe-tda10046.fw"#include <linux/init.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/device.h>#include <linux/jiffies.h>#include <linux/string.h>#include <linux/slab.h>#include "dvb_frontend.h"#include "tda1004x.h"enum tda1004x_demod {	TDA1004X_DEMOD_TDA10045,	TDA1004X_DEMOD_TDA10046,};struct tda1004x_state {	struct i2c_adapter* i2c;	struct dvb_frontend_ops ops;	const struct tda1004x_config* config;	struct dvb_frontend frontend;	/* private demod data */	u8 initialised;	enum tda1004x_demod demod_type;};static int debug;#define dprintk(args...) \	do { \		if (debug) printk(KERN_DEBUG "tda1004x: " args); \	} while (0)#define TDA1004X_CHIPID		 0x00#define TDA1004X_AUTO		 0x01#define TDA1004X_IN_CONF1	 0x02#define TDA1004X_IN_CONF2	 0x03#define TDA1004X_OUT_CONF1	 0x04#define TDA1004X_OUT_CONF2	 0x05#define TDA1004X_STATUS_CD	 0x06#define TDA1004X_CONFC4		 0x07#define TDA1004X_DSSPARE2	 0x0C#define TDA10045H_CODE_IN	 0x0D#define TDA10045H_FWPAGE	 0x0E#define TDA1004X_SCAN_CPT	 0x10#define TDA1004X_DSP_CMD	 0x11#define TDA1004X_DSP_ARG	 0x12#define TDA1004X_DSP_DATA1	 0x13#define TDA1004X_DSP_DATA2	 0x14#define TDA1004X_CONFADC1	 0x15#define TDA1004X_CONFC1		 0x16#define TDA10045H_S_AGC		 0x1a#define TDA10046H_AGC_TUN_LEVEL	 0x1a#define TDA1004X_SNR		 0x1c#define TDA1004X_CONF_TS1	 0x1e#define TDA1004X_CONF_TS2	 0x1f#define TDA1004X_CBER_RESET	 0x20#define TDA1004X_CBER_MSB	 0x21#define TDA1004X_CBER_LSB	 0x22#define TDA1004X_CVBER_LUT	 0x23#define TDA1004X_VBER_MSB	 0x24#define TDA1004X_VBER_MID	 0x25#define TDA1004X_VBER_LSB	 0x26#define TDA1004X_UNCOR		 0x27#define TDA10045H_CONFPLL_P	 0x2D#define TDA10045H_CONFPLL_M_MSB	 0x2E#define TDA10045H_CONFPLL_M_LSB	 0x2F#define TDA10045H_CONFPLL_N	 0x30#define TDA10046H_CONFPLL1	 0x2D#define TDA10046H_CONFPLL2	 0x2F#define TDA10046H_CONFPLL3	 0x30#define TDA10046H_TIME_WREF1	 0x31#define TDA10046H_TIME_WREF2	 0x32#define TDA10046H_TIME_WREF3	 0x33#define TDA10046H_TIME_WREF4	 0x34#define TDA10046H_TIME_WREF5	 0x35#define TDA10045H_UNSURW_MSB	 0x31#define TDA10045H_UNSURW_LSB	 0x32#define TDA10045H_WREF_MSB	 0x33#define TDA10045H_WREF_MID	 0x34#define TDA10045H_WREF_LSB	 0x35#define TDA10045H_MUXOUT	 0x36#define TDA1004X_CONFADC2	 0x37#define TDA10045H_IOFFSET	 0x38#define TDA10046H_CONF_TRISTATE1 0x3B#define TDA10046H_CONF_TRISTATE2 0x3C#define TDA10046H_CONF_POLARITY	 0x3D#define TDA10046H_FREQ_OFFSET	 0x3E#define TDA10046H_GPIO_OUT_SEL	 0x41#define TDA10046H_GPIO_SELECT	 0x42#define TDA10046H_AGC_CONF	 0x43#define TDA10046H_AGC_THR	 0x44#define TDA10046H_AGC_RENORM	 0x45#define TDA10046H_AGC_GAINS	 0x46#define TDA10046H_AGC_TUN_MIN	 0x47#define TDA10046H_AGC_TUN_MAX	 0x48#define TDA10046H_AGC_IF_MIN	 0x49#define TDA10046H_AGC_IF_MAX	 0x4A#define TDA10046H_FREQ_PHY2_MSB	 0x4D#define TDA10046H_FREQ_PHY2_LSB	 0x4E#define TDA10046H_CVBER_CTRL	 0x4F#define TDA10046H_AGC_IF_LEVEL	 0x52#define TDA10046H_CODE_CPT	 0x57#define TDA10046H_CODE_IN	 0x58static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data){	int ret;	u8 buf[] = { reg, data };	struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };	dprintk("%s: reg=0x%x, data=0x%x\n", __FUNCTION__, reg, data);	msg.addr = state->config->demod_address;	ret = i2c_transfer(state->i2c, &msg, 1);	if (ret != 1)		dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",			__FUNCTION__, reg, data, ret);	dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__,		reg, data, ret);	return (ret != 1) ? -1 : 0;}static int tda1004x_read_byte(struct tda1004x_state *state, int reg){	int ret;	u8 b0[] = { reg };	u8 b1[] = { 0 };	struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 },				{ .flags = I2C_M_RD, .buf = b1, .len = 1 }};	dprintk("%s: reg=0x%x\n", __FUNCTION__, reg);	msg[0].addr = state->config->demod_address;	msg[1].addr = state->config->demod_address;	ret = i2c_transfer(state->i2c, msg, 2);	if (ret != 2) {		dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg,			ret);		return -1;	}	dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__,		reg, b1[0], ret);	return b1[0];}static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data){	int val;	dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __FUNCTION__, reg,		mask, data);	// read a byte and check	val = tda1004x_read_byte(state, reg);	if (val < 0)		return val;	// mask if off	val = val & ~mask;	val |= data & 0xff;	// write it out again	return tda1004x_write_byteI(state, reg, val);}static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len){	int i;	int result;	dprintk("%s: reg=0x%x, len=0x%x\n", __FUNCTION__, reg, len);	result = 0;	for (i = 0; i < len; i++) {		result = tda1004x_write_byteI(state, reg + i, buf[i]);		if (result != 0)			break;	}	return result;}static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state){	int result;	dprintk("%s\n", __FUNCTION__);	result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2);	msleep(1);	return result;}static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state){	dprintk("%s\n", __FUNCTION__);	return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0);}static int tda10045h_set_bandwidth(struct tda1004x_state *state,				   fe_bandwidth_t bandwidth){	static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f };	static u8 bandwidth_7mhz[] = { 0x02, 0x00, 0x37, 0x00, 0x4a, 0x2f, 0x6d, 0x76, 0xdb };	static u8 bandwidth_8mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x48, 0x17, 0x89, 0xc7, 0x14 };	switch (bandwidth) {	case BANDWIDTH_6_MHZ:		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz));		break;	case BANDWIDTH_7_MHZ:		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz));		break;	case BANDWIDTH_8_MHZ:		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz));		break;	default:		return -EINVAL;	}	tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0);	return 0;}static int tda10046h_set_bandwidth(struct tda1004x_state *state,				   fe_bandwidth_t bandwidth){	static u8 bandwidth_6mhz[] = { 0x80, 0x15, 0xfe, 0xab, 0x8e };	static u8 bandwidth_7mhz[] = { 0x6e, 0x02, 0x53, 0xc8, 0x25 };	static u8 bandwidth_8mhz[] = { 0x60, 0x12, 0xa8, 0xe4, 0xbd };	switch (bandwidth) {	case BANDWIDTH_6_MHZ:		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz));		if (state->config->if_freq == TDA10046_FREQ_045) {			tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x09);			tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x4f);		}		break;	case BANDWIDTH_7_MHZ:		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz));		if (state->config->if_freq == TDA10046_FREQ_045) {			tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0a);			tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x79);		}		break;	case BANDWIDTH_8_MHZ:		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz));		if (state->config->if_freq == TDA10046_FREQ_045) {			tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0b);			tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xa3);		}		break;	default:		return -EINVAL;	}	return 0;}static int tda1004x_do_upload(struct tda1004x_state *state,			      unsigned char *mem, unsigned int len,			      u8 dspCodeCounterReg, u8 dspCodeInReg){	u8 buf[65];	struct i2c_msg fw_msg = { .flags = 0, .buf = buf, .len = 0 };	int tx_size;	int pos = 0;	/* clear code counter */	tda1004x_write_byteI(state, dspCodeCounterReg, 0);	fw_msg.addr = state->config->demod_address;	buf[0] = dspCodeInReg;	while (pos != len) {		// work out how much to send this time		tx_size = len - pos;		if (tx_size > 0x10)			tx_size = 0x10;		// send the chunk		memcpy(buf + 1, mem + pos, tx_size);		fw_msg.len = tx_size + 1;		if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) {			printk(KERN_ERR "tda1004x: Error during firmware upload\n");			return -EIO;		}		pos += tx_size;		dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, pos);	}	// give the DSP a chance to settle 03/10/05 Hac	msleep(100);	return 0;}static int tda1004x_check_upload_ok(struct tda1004x_state *state){	u8 data1, data2;	unsigned long timeout;	if (state->demod_type == TDA1004X_DEMOD_TDA10046) {		timeout = jiffies + 2 * HZ;		while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) {			if (time_after(jiffies, timeout)) {				printk(KERN_ERR "tda1004x: timeout waiting for DSP ready\n");				break;			}			msleep(1);		}	} else		msleep(100);	// check upload was OK	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP	tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67);	data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1);	data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2);	if (data1 != 0x67 || data2 < 0x20 || data2 > 0x2e) {		printk(KERN_INFO "tda1004x: found firmware revision %x -- invalid\n", data2);		return -EIO;	}	printk(KERN_INFO "tda1004x: found firmware revision %x -- ok\n", data2);	return 0;}static int tda10045_fwupload(struct dvb_frontend* fe){	struct tda1004x_state* state = fe->demodulator_priv;	int ret;	const struct firmware *fw;	/* don't re-upload unless necessary */	if (tda1004x_check_upload_ok(state) == 0)		return 0;	/* request the firmware, this will block until someone uploads it */	printk(KERN_INFO "tda1004x: waiting for firmware upload (%s)...\n", TDA10045_DEFAULT_FIRMWARE);	ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);	if (ret) {		printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");		return ret;	}	/* reset chip */	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0);	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);	msleep(10);	/* set parameters */	tda10045h_set_bandwidth(state, BANDWIDTH_8_MHZ);	ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN);	release_firmware(fw);	if (ret)		return ret;	printk(KERN_INFO "tda1004x: firmware upload complete\n");	/* wait for DSP to initialise */	/* DSPREADY doesn't seem to work on the TDA10045H */	msleep(100);	return tda1004x_check_upload_ok(state);}static void tda10046_init_plls(struct dvb_frontend* fe){	struct tda1004x_state* state = fe->demodulator_priv;	tda1004x_write_byteI(state, TDA10046H_CONFPLL1, 0xf0);	tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x0a); // PLL M = 10	if (state->config->xtal_freq == TDA10046_XTAL_4M ) {		dprintk("%s: setting up PLLs for a 4 MHz Xtal\n", __FUNCTION__);		tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0	} else {		dprintk("%s: setting up PLLs for a 16 MHz Xtal\n", __FUNCTION__);		tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 3); // PLL P = 0, N = 3	}	tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99);	switch (state->config->if_freq) {	case TDA10046_FREQ_3617:

⌨️ 快捷键说明

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