/* Driver for Philips tda10046H OFDM Frontend with LG INNOTEK TdtpE001P Tuner, based on Philips tda1004xh OFDM Frontend driver written by Andrew de Quincey & Robert Schlabbach Written by Dany Salman -- salmandany@yahoo.fr Copyright (c) 2004 TDF 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. */ //==================================================================== // Standard Include files #include #include #include #include #include #include #include #include #include #include #include //==================================================================== // DVB Include files #include "dvb_frontend.h" #include "tmbsl10046DSPcode.h" #include "tda1004x.h" #define TDA1004X_DEMOD_TDA10045 0 #define TDA1004X_DEMOD_TDA10046 1 //==================================================================== // Defines #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 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 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 TDA1004X_IT_SEL 0x29 #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 TDA1004X_CONFADC2 0x37 #define TDA10046H_CONF_TRISTATE1 0x3B #define TDA10046H_CONF_TRISTATE2 0x3C #define TDA10046H_CONF_POLARITY 0x3D #define TDA10046H_FREQ_OFFSET 0x3E #define TDA10046H_GPIO_SP_DS3 0x40 #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_CHANNEL_INFO1 0x50 #define TDA10046H_AGC_IF_LEVEL 0x52 #define TDA10046H_CODE_CPT 0x57 #define TDA10046H_CODE_IN 0x58 #define TDA10046_MAX_UNITS 1 #define TDA10046_RF_MIN 170000 #define TDA10046_RF_MAX 887000000 #define TDA10046_CS_MIN 6000000 #define TDA10046_CS_MAX 8000000 #define DEMOD_I2C_ADDRESS 0x10 >> 1 #define TUNER_I2C_ADDRESS 0xC2 >> 1 #define EEPROM_I2C_ADDRESS 0xA0 //==================================================================== // Module parameters MODULE_DESCRIPTION("Philips TDA10046H DVB-T Frontend"); MODULE_AUTHOR("Dany Salman "); MODULE_LICENSE("GPL"); #ifdef PCMCIA_DEBUG INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG); #define DEBUG(n, args...) if (pc_debug>(n)) DEBUG(0,KERN_DEBUG args) static char *version = "pluto_cs.c 1.10 2004/07/22 (Dany Salman)"; #else #define DEBUG(n, args...) #endif struct tda1004x_state { struct i2c_adapter* i2c; struct dvb_frontend_ops ops; const struct tda1004x_config* config; struct dvb_frontend frontend; u8 initialised:1; u8 demod_type; }; // Information about frontend capabilities and frequencies limit // called by ioctl FE_GET_INFO //==================================================================== // Functions // Write the byte to the register of the demodulator static int tda10046_write_byte(struct i2c_adapter *i2c, int reg, int data) { int ret; u8 buf[] = { reg, data }; struct i2c_msg msg = { .addr=0, .flags=0, .buf=buf, .len=2 }; DEBUG(1,"%s: reg=0x%x, data=0x%x\n", __FUNCTION__, reg, data); msg.addr = DEMOD_I2C_ADDRESS; ret = i2c_transfer(i2c, &msg, 1); if (ret != 1) DEBUG(0,"%s: error reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__, reg, data, ret); DEBUG(1,"%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__, reg, data, ret); return (ret != 1) ? -1 : 0; } // Read the byte contained in register static int tda1004x_read_byte(struct i2c_adapter *i2c, 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}}; DEBUG(1,"%s: reg=0x%x\n", __FUNCTION__, reg); msg[0].addr = DEMOD_I2C_ADDRESS; msg[1].addr = DEMOD_I2C_ADDRESS; ret = i2c_transfer(i2c, msg, 2); if (ret != 2) { DEBUG(0,"%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg, ret); return -1; } DEBUG(1,"%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__, reg, b1[0], ret); return b1[0]; } // Write byte data masked by in register static int tda1004x_write_mask(struct i2c_adapter *i2c, int reg, int mask, int data) { int val; DEBUG(1,"%s: reg=0x%x, mask=0x%x, data=0x%x\n", __FUNCTION__, reg, mask, data); // read a byte and check val = tda1004x_read_byte(i2c, reg); if (val < 0) return val; // mask if off val = val & ~mask; val |= data & 0xff; // write it out again return tda10046_write_byte(i2c, reg, val); } // Write bytes contained by in register static int tda1004x_write_buf(struct i2c_adapter *i2c, int reg, unsigned char *buf, int len) { int i; int result; DEBUG(1,"%s: reg=0x%x, len=0x%x\n", __FUNCTION__, reg, len); result = 0; for (i = 0; i < len; i++) { result = tda10046_write_byte(i2c, reg + i, buf[i]); if (result != 0) break; } return result; } // Set bandwitdh static int tda10046h_set_bandwidth(struct i2c_adapter *i2c, 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(i2c, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz)); tda10046_write_byte(i2c, TDA1004X_DSSPARE2, 0); break; case BANDWIDTH_7_MHZ: tda1004x_write_buf(i2c, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz)); tda10046_write_byte(i2c, TDA1004X_DSSPARE2, 0); break; case BANDWIDTH_8_MHZ: tda1004x_write_buf(i2c, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz)); tda10046_write_byte(i2c, TDA1004X_DSSPARE2, 0xFF); break; default: return -EINVAL; } // done return 0; } // Upload the firmware static int tda1004x_fwupload(struct i2c_adapter *i2c) { u8 dsp_data1, dsp_data2; u32 totalsize = 0; struct i2c_msg fw_msg ; fw_msg.addr = DEMOD_I2C_ADDRESS; fw_msg.flags = 0 ; printk("Please wait while uploading firmware...\n"); // First calculate the size of code included in the firmware totalsize += (u32)((DSPcode10046RAM[0] << 8) | DSPcode10046RAM[1]) * 2; totalsize += 4; totalsize += (u32)((DSPcode10046RAM[totalsize] << 8) | DSPcode10046RAM[totalsize + 1]) * 2; totalsize += 4; // Prepare the chip to accept a firmware uploading tda1004x_write_mask(i2c, TDA10046H_CONF_TRISTATE1, 1, 0); tda1004x_write_mask(i2c, TDA1004X_CONFC4, 8, 8); tda10046_write_byte(i2c, TDA10046H_CODE_CPT, 0x00); // Prepare the message fw_msg.buf = vmalloc(totalsize + 1); fw_msg.len = totalsize + 1; fw_msg.buf[0] = TDA10046H_CODE_IN; // Copy the content of array DSPcode10046RAM into message buffer memcpy(fw_msg.buf + 1, DSPcode10046RAM, totalsize); // Upload firmware into TDA10046H_CODE_IN if (i2c_transfer(i2c, &fw_msg, 1) != 1) { printk("tda1004x: Error during firmware upload\n"); vfree (fw_msg.buf); return -EIO; } vfree (fw_msg.buf); // First test to check if firmware has correctly been uploaded tda10046_write_byte(i2c, TDA1004X_DSP_CMD, 0x61); dsp_data1 = tda1004x_read_byte(i2c, TDA1004X_DSP_DATA1); dsp_data2 = tda1004x_read_byte(i2c, TDA1004X_DSP_DATA2); if ((dsp_data1 != 0) || (dsp_data2 != 0)) return -EIO; // Second test to check if firmware has correctly been uploaded tda10046_write_byte(i2c, TDA1004X_DSP_CMD, 0x67); dsp_data1 = tda1004x_read_byte(i2c, TDA1004X_DSP_DATA1); dsp_data2 = tda1004x_read_byte(i2c, TDA1004X_DSP_DATA2); if (dsp_data1 != 0x67) return -EIO; // So far, we consider all was fine printk("Firmware uploaded successfully !\n"); return 0; } // Process the FEC static int tda1004x_encode_fec(int fec) { // convert known FEC values switch (fec) { case FEC_1_2: return 0; case FEC_2_3: return 1; case FEC_3_4: return 2; case FEC_5_6: return 3; case FEC_7_8: return 4; } // unsupported return -EINVAL; } static int tda1004x_decode_fec(int tdafec) { // convert known FEC values switch (tdafec) { case 0: return FEC_1_2; case 1: return FEC_2_3; case 2: return FEC_3_4; case 3: return FEC_5_6; case 4: return FEC_7_8; } // unsupported return -1; } // Setup new frequency into tuner static int tda1004x_set_frequency(struct i2c_adapter *i2c, struct dvb_frontend_parameters *fe_params) { u8 tuner_buf[4]; struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) }; u32 tuner_frequency = 0; u8 tmp; DEBUG(0,"%s\n", __FUNCTION__); // setup auto offset tda1004x_write_mask(i2c, TDA1004X_AUTO, 0x10, 0x10); tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x80, 0); tda1004x_write_mask(i2c, TDA1004X_IN_CONF2, 0xC0, 0); // disable agc_conf[2] tda1004x_write_mask(i2c, TDA10046H_AGC_CONF, 4, 0); //tda1004x_enable_tuner_i2c(i2c); tda1004x_write_mask(i2c, TDA1004X_CONFC4, 2, 2); // setup the frequency buffer tuner_frequency = (((fe_params->frequency / 1000) * 6) + 217502) / 1000; tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; tuner_buf[1] = tuner_frequency & 0xff; if (fe_params->frequency < 611000000) tuner_buf[2] = 0xb4; else if (fe_params->frequency < 811000000) tuner_buf[2] = 0xbc; else tuner_buf[2] = 0xf4; if (fe_params->frequency < 470000000) tuner_buf[3] = 0x02; else if (fe_params->frequency < 823000000) tuner_buf[3] = 0x04; if (fe_params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) tuner_buf[3] |= 0x08; tuner_msg.addr = TUNER_I2C_ADDRESS; tuner_msg.len = 4; if ((tmp = i2c_transfer(i2c, &tuner_msg, 1)) != 1) { printk("I2C Tuner error : tmp %d, addr 0x%02x, buf[0] 0x%02x, buf[1] 0x%02x, buf[2] 0x%02x, buf[3] 0x%02x\n",tmp,tuner_msg.addr,tuner_msg.buf[0],tuner_msg.buf[1],tuner_msg.buf[2],tuner_msg.buf[3]); return -EIO; } msleep(5); tda1004x_write_mask(i2c, TDA1004X_CONFC4, 2, 0); tda1004x_write_mask(i2c, TDA10046H_AGC_CONF, 4, 4); DEBUG(0,"%s: success\n", __FUNCTION__); // done return 0; } // Setup new frontend parameters (FE_SET_FRONTEND ioctl) static int tda1004x_set_fe(struct dvb_frontend *fe, struct dvb_frontend_parameters *fe_params) { struct tda1004x_state *state = fe->demodulator_priv; struct i2c_adapter *i2c = state->i2c; int tmp; int inversion; DEBUG(0,"%s\n", __FUNCTION__); // set frequency if ((tmp = tda1004x_set_frequency(i2c, fe_params)) < 0) return tmp; // hardcoded to use auto as much as possible fe_params->u.ofdm.code_rate_HP = FEC_AUTO; fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; // Set standard params.. or put them to auto if ((fe_params->u.ofdm.code_rate_HP == FEC_AUTO) || (fe_params->u.ofdm.code_rate_LP == FEC_AUTO) || (fe_params->u.ofdm.constellation == QAM_AUTO) || (fe_params->u.ofdm.hierarchy_information == HIERARCHY_AUTO)) { tda1004x_write_mask(i2c, TDA1004X_AUTO, 1, 1); // enable auto tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x03, 0); // turn off constellation bits tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits tda1004x_write_mask(i2c, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits } else { tda1004x_write_mask(i2c, TDA1004X_AUTO, 1, 0); // disable auto // set HP FEC tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_HP); if (tmp < 0) return tmp; tda1004x_write_mask(i2c, TDA1004X_IN_CONF2, 7, tmp); // set LP FEC if (fe_params->u.ofdm.code_rate_LP != FEC_NONE) { tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_LP); if (tmp < 0) return tmp; tda1004x_write_mask(i2c, TDA1004X_IN_CONF2, 0x38, tmp << 3); } // set constellation switch (fe_params->u.ofdm.constellation) { case QPSK: tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 3, 0); break; case QAM_16: tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 3, 1); break; case QAM_64: tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 3, 2); break; default: return -EINVAL; } // set hierarchy switch (fe_params->u.ofdm.hierarchy_information) { case HIERARCHY_NONE: tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x60, 0 << 5); break; case HIERARCHY_1: tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x60, 1 << 5); break; case HIERARCHY_2: tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x60, 2 << 5); break; case HIERARCHY_4: tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x60, 3 << 5); break; default: return -EINVAL; } } // set bandwidth tda10046h_set_bandwidth(i2c, fe_params->u.ofdm.bandwidth); // need to invert the inversion for TT TDA10046H inversion = fe_params->inversion; inversion = inversion ? INVERSION_OFF : INVERSION_ON; // set inversion switch (inversion) { case INVERSION_OFF: tda1004x_write_mask(i2c, TDA1004X_CONFC1, 0x20, 0); break; case INVERSION_ON: tda1004x_write_mask(i2c, TDA1004X_CONFC1, 0x20, 0x20); break; default: return -EINVAL; } // set guard interval switch (fe_params->u.ofdm.guard_interval) { case GUARD_INTERVAL_1_32: tda1004x_write_mask(i2c, TDA1004X_AUTO, 2, 0); tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x0c, 0 << 2); break; case GUARD_INTERVAL_1_16: tda1004x_write_mask(i2c, TDA1004X_AUTO, 2, 0); tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x0c, 1 << 2); break; case GUARD_INTERVAL_1_8: tda1004x_write_mask(i2c, TDA1004X_AUTO, 2, 0); tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x0c, 2 << 2); break; case GUARD_INTERVAL_1_4: tda1004x_write_mask(i2c, TDA1004X_AUTO, 2, 0); tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x0c, 3 << 2); break; case GUARD_INTERVAL_AUTO: tda1004x_write_mask(i2c, TDA1004X_AUTO, 2, 2); tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x0c, 0 << 2); break; default: return -EINVAL; } // set transmission mode switch (fe_params->u.ofdm.transmission_mode) { case TRANSMISSION_MODE_2K: tda1004x_write_mask(i2c, TDA1004X_AUTO, 4, 0); tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x10, 0 << 4); break; case TRANSMISSION_MODE_8K: tda1004x_write_mask(i2c, TDA1004X_AUTO, 4, 0); tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x10, 1 << 4); break; case TRANSMISSION_MODE_AUTO: tda1004x_write_mask(i2c, TDA1004X_AUTO, 4, 4); tda1004x_write_mask(i2c, TDA1004X_IN_CONF1, 0x10, 0); break; default: return -EINVAL; } // start the lock tda1004x_write_mask(i2c, TDA1004X_AUTO, 0x40, 0x40); msleep(10); // done return 0; } // Get frontend parameters (FE_GET_FRONTEND ioctl) static int tda1004x_get_fe(struct dvb_frontend *fe, struct dvb_frontend_parameters *fe_params) { struct tda1004x_state *state = fe->demodulator_priv; struct i2c_adapter *i2c = state->i2c; DEBUG(0,"%s\n", __FUNCTION__); // inversion status fe_params->inversion = INVERSION_OFF; if (tda1004x_read_byte(i2c, TDA1004X_CONFC1) & 0x20) fe_params->inversion = INVERSION_ON; // need to invert the inversion for TT TDA10046H fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON; // bandwidth switch (tda1004x_read_byte(i2c, TDA10046H_TIME_WREF1)) { case 0x60: fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; break; case 0x6e: fe_params->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; break; case 0x80: fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; break; } // FEC fe_params->u.ofdm.code_rate_HP = tda1004x_decode_fec(tda1004x_read_byte(i2c, TDA1004X_OUT_CONF2) & 7); fe_params->u.ofdm.code_rate_LP = tda1004x_decode_fec((tda1004x_read_byte(i2c, TDA1004X_OUT_CONF2) >> 3) & 7); // constellation switch (tda1004x_read_byte(i2c, TDA1004X_OUT_CONF1) & 3) { case 0: fe_params->u.ofdm.constellation = QPSK; break; case 1: fe_params->u.ofdm.constellation = QAM_16; break; case 2: fe_params->u.ofdm.constellation = QAM_64; break; } // transmission mode fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; if (tda1004x_read_byte(i2c, TDA1004X_OUT_CONF1) & 0x10) fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; // guard interval switch ((tda1004x_read_byte(i2c, TDA1004X_OUT_CONF1) & 0x0c) >> 2) { case 0: fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; case 1: fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break; case 2: fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break; case 3: fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break; } // hierarchy switch ((tda1004x_read_byte(i2c, TDA1004X_OUT_CONF1) & 0x60) >> 5) { case 0: fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE; break; case 1: fe_params->u.ofdm.hierarchy_information = HIERARCHY_1; break; case 2: fe_params->u.ofdm.hierarchy_information = HIERARCHY_2; break; case 3: fe_params->u.ofdm.hierarchy_information = HIERARCHY_4; break; } // done return 0; } // Read chip status (FE_READ_STATUS ioctl) static int tda1004x_read_status(struct dvb_frontend *fe, fe_status_t * fe_status) { struct tda1004x_state *state = fe->demodulator_priv; struct i2c_adapter *i2c = state->i2c; int status; int cber; DEBUG(0,"%s\n", __FUNCTION__); // read status status = tda1004x_read_byte(i2c, TDA1004X_STATUS_CD); if (status == -1) return -EIO; // decode *fe_status = 0; if (status & 4) *fe_status |= FE_HAS_SIGNAL; if (status & 2) *fe_status |= FE_HAS_CARRIER; if (status & 8) *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; // if we don't already have VITERBI (i.e. not LOCKED), see if the viterbi // is getting anything valid if (!(*fe_status & FE_HAS_VITERBI)) { // read the CBER cber = tda1004x_read_byte(i2c, TDA1004X_CBER_LSB); if (cber == -1) return -EIO; status = tda1004x_read_byte(i2c, TDA1004X_CBER_MSB); if (status == -1) return -EIO; cber |= (status << 8); tda1004x_read_byte(i2c, TDA1004X_CBER_RESET); if (cber != 65535) *fe_status |= FE_HAS_VITERBI; } /*// if we DO have some valid VITERBI output, but don't already have SYNC // bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid. if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) { // read the VBER vber = tda1004x_read_byte(i2c, TDA1004X_VBER_LSB); if (vber == -1) return -EIO; status = tda1004x_read_byte(i2c, TDA1004X_VBER_MID); if (status == -1) return -EIO; vber |= (status << 8); status = tda1004x_read_byte(i2c, TDA1004X_VBER_MSB); if (status == -1) return -EIO; vber |= ((status << 16) & 0x0f); tda1004x_read_byte(i2c, TDA1004X_CVBER_LUT); // if RS has passed some valid TS packets, then we must be // getting some SYNC bytes if (vber < 16632) *fe_status |= FE_HAS_SYNC; }*/ // success DEBUG(0,"%s: fe_status=0x%x\n", __FUNCTION__, *fe_status); return 0; } // Read the signal strength (FE_READ_SIGNAL_STRENGTH ioctl) static int tda1004x_read_signal_strength(struct dvb_frontend *fe, u16 *signal) { struct tda1004x_state *state = fe->demodulator_priv; struct i2c_adapter *i2c = state->i2c; int tmp; int reg = 0; DEBUG(0,"%s\n", __FUNCTION__); reg = TDA10046H_AGC_IF_LEVEL; // read it tmp = tda1004x_read_byte(i2c, reg); if (tmp < 0) return -EIO; // done *signal = (tmp << 8) | tmp; DEBUG(0,"%s: signal=0x%x\n", __FUNCTION__, *signal); return 0; } // Read the signal noise ratio (FE_READ_SNR ioctl) static int tda1004x_read_snr(struct dvb_frontend *fe, u16 *snr) { struct tda1004x_state *state = fe->demodulator_priv; struct i2c_adapter *i2c = state->i2c; int tmp; DEBUG(0,"%s\n", __FUNCTION__); // read it tmp = tda1004x_read_byte(i2c, TDA1004X_SNR); if (tmp < 0) return -EIO; if (tmp) { tmp = 255 - tmp; } // done *snr = ((tmp << 8) | tmp); DEBUG(0,"%s: snr=0x%x\n", __FUNCTION__, *snr); return 0; } // Read the number of uncorrected blocks (FE_READ_UNCORRECTED_BLOCKS ioctl) static int tda1004x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { struct tda1004x_state *state = fe->demodulator_priv; struct i2c_adapter *i2c = state->i2c; int tmp; int tmp2; int counter; DEBUG(0,"%s\n", __FUNCTION__); // read the UCBLOCKS and reset counter = 0; tmp = tda1004x_read_byte(i2c, TDA1004X_UNCOR); if (tmp < 0) return -EIO; tmp &= 0x7f; while (counter++ < 5) { tda1004x_write_mask(i2c, TDA1004X_UNCOR, 0x80, 0); tda1004x_write_mask(i2c, TDA1004X_UNCOR, 0x80, 0); tda1004x_write_mask(i2c, TDA1004X_UNCOR, 0x80, 0); tmp2 = tda1004x_read_byte(i2c, TDA1004X_UNCOR); if (tmp2 < 0) return -EIO; tmp2 &= 0x7f; if ((tmp2 < tmp) || (tmp2 == 0)) break; } // done if (tmp != 0x7f) *ucblocks = tmp; else *ucblocks = 0xffffffff; DEBUG(0,"%s: ucblocks=0x%x\n", __FUNCTION__, *ucblocks); return 0; } // Read the Bit Error Rate (FE_READ_BER ioctl) static int tda1004x_read_ber(struct dvb_frontend *fe, u32* ber) { struct tda1004x_state *state = fe->demodulator_priv; struct i2c_adapter *i2c = state->i2c; int tmp; DEBUG(0,"%s\n", __FUNCTION__); // read it in tmp = tda1004x_read_byte(i2c, TDA1004X_CBER_LSB); if (tmp < 0) return -EIO; *ber = tmp << 1; tmp = tda1004x_read_byte(i2c, TDA1004X_CBER_MSB); if (tmp < 0) return -EIO; *ber |= (tmp << 9); tda1004x_read_byte(i2c, TDA1004X_CBER_RESET); // done DEBUG(0,"%s: ber=0x%x\n", __FUNCTION__, *ber); return 0; } // Initialize demodulator (FE_INIT ioctl) static int tda10046_init(struct dvb_frontend *fe) { struct tda1004x_state *state = fe->demodulator_priv; struct i2c_adapter *i2c = state->i2c; int status = 0; status |= tda10046_write_byte(i2c, TDA10046H_CONFPLL2, 0xa); status |= tda10046_write_byte(i2c, TDA10046H_CONFPLL3, 0x03); status |= tda10046_write_byte(i2c, TDA10046H_FREQ_OFFSET, 0x64); status |= tda10046_write_byte(i2c, TDA10046H_FREQ_PHY2_MSB, 0xd4); status |= tda10046_write_byte(i2c, TDA10046H_FREQ_PHY2_LSB, 0x2a); status |= tda10046_write_byte(i2c, TDA10046H_TIME_WREF1, 0x60); status |= tda10046_write_byte(i2c, TDA10046H_TIME_WREF2, 0x12); status |= tda10046_write_byte(i2c, TDA10046H_TIME_WREF3, 0xa8); status |= tda10046_write_byte(i2c, TDA10046H_TIME_WREF4, 0xe4); status |= tda10046_write_byte(i2c, TDA10046H_TIME_WREF5, 0xbd); status |= tda1004x_write_mask(i2c, TDA1004X_CONFC4, 0x20, 0); status |= tda1004x_write_mask(i2c, TDA1004X_CONFC1, 0xa0, 0x20); status |= tda10046_write_byte(i2c, TDA10046H_AGC_CONF, 0); status |= tda1004x_write_mask(i2c, TDA10046H_CONF_POLARITY, 0x60, 0x60); status |= tda10046_write_byte(i2c, TDA10046H_AGC_TUN_MIN, 0); status |= tda10046_write_byte(i2c, TDA10046H_AGC_TUN_MAX, 0xff); status |= tda10046_write_byte(i2c, TDA10046H_AGC_IF_MIN, 0); status |= tda10046_write_byte(i2c, TDA10046H_AGC_IF_MAX, 0xff); status |= tda1004x_write_mask(i2c, TDA10046H_CVBER_CTRL, 0x30, 0x20); status |= tda1004x_write_mask(i2c, TDA1004X_IT_SEL, 0x08, 0x08); status |= tda10046_write_byte(i2c, TDA10046H_AGC_GAINS, 0x1); status |= tda1004x_write_mask(i2c, TDA1004X_AUTO, 0x80, 0); status |= tda10046_write_byte(i2c, TDA1004X_CONF_TS1, 0x7); status |= tda1004x_write_mask(i2c, TDA1004X_CONF_TS2, 0x31, 0); status |= tda1004x_write_mask(i2c, TDA10046H_CONF_TRISTATE1, 0x9e, 0); status |= tda10046_write_byte(i2c, TDA10046H_CONF_TRISTATE2, 0xbc); status |= tda10046_write_byte(i2c, TDA10046H_GPIO_OUT_SEL, 0xfc); status |= tda10046_write_byte(i2c, TDA10046H_GPIO_SP_DS3, 0); status |= tda10046_write_byte(i2c, TDA10046H_CHANNEL_INFO1, 0); status |= tda10046_write_byte(i2c, TDA1004X_CONFADC2, 0x74); status |= tda10046_write_byte(i2c, TDA1004X_CONFADC2, 0x34); // upload firmware DEBUG(0,"%s: Uploading firmware",__FUNCTION__); status |= tda1004x_fwupload(i2c); return status; } // Disable tuner activity (FE_SLEEP ioctl) static int tda1004x_sleep(struct dvb_frontend *fe) { // Do nothing... for the moment //tda1004x_write_mask(i2c, TDA1004X_CONFC4, 1, 1); return 0; } static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { fesettings->min_delay_ms = 800; fesettings->step_size = 166667; fesettings->max_drift = 166667*2; return 0; } static void tda1004x_release(struct dvb_frontend *fe) { struct tda1004x_state *state = fe->demodulator_priv; kfree(state); } static struct dvb_frontend_ops tda10046_ops = { .info = { .name = "Philips TDA10046H DVB-T", .type = FE_OFDM, .frequency_min = TDA10046_RF_MIN, .frequency_max = TDA10046_RF_MAX, .frequency_stepsize = 166667, .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_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO }, .release = tda1004x_release, .init = tda10046_init, .sleep = tda1004x_sleep, .set_frontend = tda1004x_set_fe, .get_frontend = tda1004x_get_fe, .get_tune_settings = tda1004x_get_tune_settings, .read_status = tda1004x_read_status, .read_ber = tda1004x_read_ber, .read_signal_strength = tda1004x_read_signal_strength, .read_snr = tda1004x_read_snr, .read_ucblocks = tda1004x_read_ucblocks, }; struct dvb_frontend *tda10046_attach(const struct tda1004x_config *config, struct i2c_adapter *i2c) { struct tda1004x_state* state; DEBUG(0,"%s\n", __FUNCTION__); state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); if (!state) goto error; state->config = config; state->i2c = i2c; memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops)); state->initialised = 0; state->demod_type = TDA1004X_DEMOD_TDA10046; if (tda1004x_read_byte(i2c, TDA1004X_CHIPID) != 0x46) goto error; state->frontend.ops = &state->ops; state->frontend.demodulator_priv = state; return &state->frontend; error: kfree(state); return NULL; } EXPORT_SYMBOL(tda10046_attach);