📄 tda1004x.c
字号:
/* 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 "dvb_frontend.h"#include "tda1004x.h"#define TDA1004X_DEMOD_TDA10045 0#define TDA1004X_DEMOD_TDA10046 1struct 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:1; u8 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_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 = { .addr=0, .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[] = {{ .addr=0, .flags=0, .buf=b0, .len=1}, { .addr=0, .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)); break; case BANDWIDTH_7_MHZ: tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz)); break; case BANDWIDTH_8_MHZ: tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz)); 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 = {.addr = 0,.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("tda1004x: Error during firmware upload\n"); return -EIO; } pos += tx_size; dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, pos); } return 0;}static int tda1004x_check_upload_ok(struct tda1004x_state *state, u8 dspVersion){ u8 data1, data2; // 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 != dspVersion) { return -EIO; } 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, 0x2c) == 0) return 0; /* request the firmware, this will block until someone uploads it */ printk("tda1004x: waiting for firmware upload...\n"); ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); if (ret) { printk("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); if (ret) return ret; /* wait for DSP to initialise */ /* DSPREADY doesn't seem to work on the TDA10045H */ msleep(100); return tda1004x_check_upload_ok(state, 0x2c);}static int tda10046_fwupload(struct dvb_frontend* fe){ struct tda1004x_state* state = fe->demodulator_priv; unsigned long timeout; int ret; const struct firmware *fw; /* reset + wake up chip */ tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0); tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0); msleep(100); /* don't re-upload unless necessary */ if (tda1004x_check_upload_ok(state, 0x20) == 0) return 0; /* request the firmware, this will block until someone uploads it */ printk("tda1004x: waiting for firmware upload...\n"); ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE); if (ret) { printk("tda1004x: no firmware upload (timeout or file not found?)\n"); return ret; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -