📄 tda10048.c
字号:
/* NXP TDA10048HN DVB OFDM demodulator driver Copyright (C) 2008 Steven Toth <stoth@linuxtv.org> 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_math.h"#include "tda10048.h"#define TDA10048_DEFAULT_FIRMWARE "dvb-fe-tda10048-1.0.fw"#define TDA10048_DEFAULT_FIRMWARE_SIZE 24878/* Register name definitions */#define TDA10048_IDENTITY 0x00#define TDA10048_VERSION 0x01#define TDA10048_DSP_CODE_CPT 0x0C#define TDA10048_DSP_CODE_IN 0x0E#define TDA10048_IN_CONF1 0x10#define TDA10048_IN_CONF2 0x11#define TDA10048_IN_CONF3 0x12#define TDA10048_OUT_CONF1 0x14#define TDA10048_OUT_CONF2 0x15#define TDA10048_OUT_CONF3 0x16#define TDA10048_AUTO 0x18#define TDA10048_SYNC_STATUS 0x1A#define TDA10048_CONF_C4_1 0x1E#define TDA10048_CONF_C4_2 0x1F#define TDA10048_CODE_IN_RAM 0x20#define TDA10048_CHANNEL_INFO_1_R 0x22#define TDA10048_CHANNEL_INFO_2_R 0x23#define TDA10048_CHANNEL_INFO1 0x24#define TDA10048_CHANNEL_INFO2 0x25#define TDA10048_TIME_ERROR_R 0x26#define TDA10048_TIME_ERROR 0x27#define TDA10048_FREQ_ERROR_LSB_R 0x28#define TDA10048_FREQ_ERROR_MSB_R 0x29#define TDA10048_FREQ_ERROR_LSB 0x2A#define TDA10048_FREQ_ERROR_MSB 0x2B#define TDA10048_IT_SEL 0x30#define TDA10048_IT_STAT 0x32#define TDA10048_DSP_AD_LSB 0x3C#define TDA10048_DSP_AD_MSB 0x3D#define TDA10048_DSP_REF_LSB 0x3E#define TDA10048_DSP_REF_MSB 0x3F#define TDA10048_CONF_TRISTATE1 0x44#define TDA10048_CONF_TRISTATE2 0x45#define TDA10048_CONF_POLARITY 0x46#define TDA10048_GPIO_SP_DS0 0x48#define TDA10048_GPIO_SP_DS1 0x49#define TDA10048_GPIO_SP_DS2 0x4A#define TDA10048_GPIO_SP_DS3 0x4B#define TDA10048_GPIO_OUT_SEL 0x4C#define TDA10048_GPIO_SELECT 0x4D#define TDA10048_IC_MODE 0x4E#define TDA10048_CONF_XO 0x50#define TDA10048_CONF_PLL1 0x51#define TDA10048_CONF_PLL2 0x52#define TDA10048_CONF_PLL3 0x53#define TDA10048_CONF_ADC 0x54#define TDA10048_CONF_ADC_2 0x55#define TDA10048_CONF_C1_1 0x60#define TDA10048_CONF_C1_3 0x62#define TDA10048_AGC_CONF 0x70#define TDA10048_AGC_THRESHOLD_LSB 0x72#define TDA10048_AGC_THRESHOLD_MSB 0x73#define TDA10048_AGC_RENORM 0x74#define TDA10048_AGC_GAINS 0x76#define TDA10048_AGC_TUN_MIN 0x78#define TDA10048_AGC_TUN_MAX 0x79#define TDA10048_AGC_IF_MIN 0x7A#define TDA10048_AGC_IF_MAX 0x7B#define TDA10048_AGC_TUN_LEVEL 0x7E#define TDA10048_AGC_IF_LEVEL 0x7F#define TDA10048_DIG_AGC_LEVEL 0x81#define TDA10048_FREQ_PHY2_LSB 0x86#define TDA10048_FREQ_PHY2_MSB 0x87#define TDA10048_TIME_INVWREF_LSB 0x88#define TDA10048_TIME_INVWREF_MSB 0x89#define TDA10048_TIME_WREF_LSB 0x8A#define TDA10048_TIME_WREF_MID1 0x8B#define TDA10048_TIME_WREF_MID2 0x8C#define TDA10048_TIME_WREF_MSB 0x8D#define TDA10048_NP_OUT 0xA2#define TDA10048_CELL_ID_LSB 0xA4#define TDA10048_CELL_ID_MSB 0xA5#define TDA10048_EXTTPS_ODD 0xAA#define TDA10048_EXTTPS_EVEN 0xAB#define TDA10048_TPS_LENGTH 0xAC#define TDA10048_FREE_REG_1 0xB2#define TDA10048_FREE_REG_2 0xB3#define TDA10048_CONF_C3_1 0xC0#define TDA10048_CYBER_CTRL 0xC2#define TDA10048_CBER_NMAX_LSB 0xC4#define TDA10048_CBER_NMAX_MSB 0xC5#define TDA10048_CBER_LSB 0xC6#define TDA10048_CBER_MSB 0xC7#define TDA10048_VBER_LSB 0xC8#define TDA10048_VBER_MID 0xC9#define TDA10048_VBER_MSB 0xCA#define TDA10048_CYBER_LUT 0xCC#define TDA10048_UNCOR_CTRL 0xCD#define TDA10048_UNCOR_CPT_LSB 0xCE#define TDA10048_UNCOR_CPT_MSB 0xCF#define TDA10048_SOFT_IT_C3 0xD6#define TDA10048_CONF_TS2 0xE0#define TDA10048_CONF_TS1 0xE1static unsigned int debug;#define dprintk(level, fmt, arg...)\ do { if (debug >= level)\ printk(KERN_DEBUG "tda10048: " fmt, ## arg);\ } while (0)struct tda10048_state { struct i2c_adapter *i2c; /* configuration settings */ const struct tda10048_config *config; struct dvb_frontend frontend; int fwloaded;};static struct init_tab { u8 reg; u16 data;} init_tab[] = { { TDA10048_CONF_PLL1, 0x08 }, { TDA10048_CONF_ADC_2, 0x00 }, { TDA10048_CONF_C4_1, 0x00 }, { TDA10048_CONF_PLL1, 0x0f }, { TDA10048_CONF_PLL2, 0x0a }, { TDA10048_CONF_PLL3, 0x43 }, { TDA10048_FREQ_PHY2_LSB, 0x02 }, { TDA10048_FREQ_PHY2_MSB, 0x0a }, { TDA10048_TIME_WREF_LSB, 0xbd }, { TDA10048_TIME_WREF_MID1, 0xe4 }, { TDA10048_TIME_WREF_MID2, 0xa8 }, { TDA10048_TIME_WREF_MSB, 0x02 }, { TDA10048_TIME_INVWREF_LSB, 0x04 }, { TDA10048_TIME_INVWREF_MSB, 0x06 }, { TDA10048_CONF_C4_1, 0x00 }, { TDA10048_CONF_C1_1, 0xa8 }, { TDA10048_AGC_CONF, 0x16 }, { TDA10048_CONF_C1_3, 0x0b }, { TDA10048_AGC_TUN_MIN, 0x00 }, { TDA10048_AGC_TUN_MAX, 0xff }, { TDA10048_AGC_IF_MIN, 0x00 }, { TDA10048_AGC_IF_MAX, 0xff }, { TDA10048_AGC_THRESHOLD_MSB, 0x00 }, { TDA10048_AGC_THRESHOLD_LSB, 0x70 }, { TDA10048_CYBER_CTRL, 0x38 }, { TDA10048_AGC_GAINS, 0x12 }, { TDA10048_CONF_XO, 0x00 }, { TDA10048_CONF_TS1, 0x07 }, { TDA10048_IC_MODE, 0x00 }, { TDA10048_CONF_TS2, 0xc0 }, { TDA10048_CONF_TRISTATE1, 0x21 }, { TDA10048_CONF_TRISTATE2, 0x00 }, { TDA10048_CONF_POLARITY, 0x00 }, { TDA10048_CONF_C4_2, 0x04 }, { TDA10048_CONF_ADC, 0x60 }, { TDA10048_CONF_ADC_2, 0x10 }, { TDA10048_CONF_ADC, 0x60 }, { TDA10048_CONF_ADC_2, 0x00 }, { TDA10048_CONF_C1_1, 0xa8 }, { TDA10048_UNCOR_CTRL, 0x00 }, { TDA10048_CONF_C4_2, 0x04 },};static int tda10048_writereg(struct tda10048_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 }; dprintk(2, "%s(reg = 0x%02x, data = 0x%02x)\n", __func__, reg, data); ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) printk("%s: writereg error (ret == %i)\n", __func__, ret); return (ret != 1) ? -1 : 0;}static u8 tda10048_readreg(struct tda10048_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 } }; dprintk(2, "%s(reg = 0x%02x)\n", __func__, reg); ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) printk(KERN_ERR "%s: readreg error (ret == %i)\n", __func__, ret); return b1[0];}static int tda10048_writeregbulk(struct tda10048_state *state, u8 reg, const u8 *data, u16 len){ int ret = -EREMOTEIO; struct i2c_msg msg; u8 *buf; dprintk(2, "%s(%d, ?, len = %d)\n", __func__, reg, len); buf = kmalloc(len + 1, GFP_KERNEL); if (buf == NULL) { ret = -ENOMEM; goto error; } *buf = reg; memcpy(buf + 1, data, len); msg.addr = state->config->demod_address; msg.flags = 0; msg.buf = buf; msg.len = len + 1; dprintk(2, "%s(): write len = %d\n", __func__, msg.len); ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) { printk(KERN_ERR "%s(): writereg error err %i\n", __func__, ret); ret = -EREMOTEIO; }error: kfree(buf); return ret;}static int tda10048_firmware_upload(struct dvb_frontend *fe){ struct tda10048_state *state = fe->demodulator_priv; const struct firmware *fw; int ret; int pos = 0; int cnt; u8 wlen = state->config->fwbulkwritelen; if ((wlen != TDA10048_BULKWRITE_200) && (wlen != TDA10048_BULKWRITE_50)) wlen = TDA10048_BULKWRITE_200; /* request the firmware, this will block and timeout */ printk(KERN_INFO "%s: waiting for firmware upload (%s)...\n", __func__, TDA10048_DEFAULT_FIRMWARE); ret = request_firmware(&fw, TDA10048_DEFAULT_FIRMWARE, &state->i2c->dev); if (ret) { printk(KERN_ERR "%s: Upload failed. (file not found?)\n", __func__); return -EIO; } else { printk(KERN_INFO "%s: firmware read %Zu bytes.\n", __func__, fw->size); ret = 0; } if (fw->size != TDA10048_DEFAULT_FIRMWARE_SIZE) { printk(KERN_ERR "%s: firmware incorrect size\n", __func__); ret = -EIO; } else { printk(KERN_INFO "%s: firmware uploading\n", __func__); /* Soft reset */ tda10048_writereg(state, TDA10048_CONF_TRISTATE1, tda10048_readreg(state, TDA10048_CONF_TRISTATE1) & 0xfe); tda10048_writereg(state, TDA10048_CONF_TRISTATE1, tda10048_readreg(state, TDA10048_CONF_TRISTATE1) | 0x01); /* Put the demod into host download mode */ tda10048_writereg(state, TDA10048_CONF_C4_1, tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xf9); /* Boot the DSP */ tda10048_writereg(state, TDA10048_CONF_C4_1, tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x08); /* Prepare for download */ tda10048_writereg(state, TDA10048_DSP_CODE_CPT, 0); /* Download the firmware payload */ while (pos < fw->size) { if ((fw->size - pos) > wlen) cnt = wlen; else cnt = fw->size - pos; tda10048_writeregbulk(state, TDA10048_DSP_CODE_IN, &fw->data[pos], cnt); pos += cnt; } ret = -EIO; /* Wait up to 250ms for the DSP to boot */ for (cnt = 0; cnt < 250 ; cnt += 10) { msleep(10); if (tda10048_readreg(state, TDA10048_SYNC_STATUS) & 0x40) { ret = 0; break; } } } release_firmware(fw); if (ret == 0) { printk(KERN_INFO "%s: firmware uploaded\n", __func__); state->fwloaded = 1; } else printk(KERN_ERR "%s: firmware upload failed\n", __func__); return ret;}static int tda10048_set_inversion(struct dvb_frontend *fe, int inversion){ struct tda10048_state *state = fe->demodulator_priv; dprintk(1, "%s(%d)\n", __func__, inversion); if (inversion == TDA10048_INVERSION_ON) tda10048_writereg(state, TDA10048_CONF_C1_1, tda10048_readreg(state, TDA10048_CONF_C1_1) | 0x20); else tda10048_writereg(state, TDA10048_CONF_C1_1, tda10048_readreg(state, TDA10048_CONF_C1_1) & 0xdf); return 0;}/* Retrieve the demod settings */static int tda10048_get_tps(struct tda10048_state *state, struct dvb_ofdm_parameters *p){ u8 val; /* Make sure the TPS regs are valid */ if (!(tda10048_readreg(state, TDA10048_AUTO) & 0x01)) return -EAGAIN; val = tda10048_readreg(state, TDA10048_OUT_CONF2); switch ((val & 0x60) >> 5) { case 0: p->constellation = QPSK; break; case 1: p->constellation = QAM_16; break; case 2: p->constellation = QAM_64; break; } switch ((val & 0x18) >> 3) { 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; } switch (val & 0x07) { 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; } val = tda10048_readreg(state, TDA10048_OUT_CONF3); 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -