Commit b4da6ccc authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] dvb: tda1004x DVB frontend update

From: Michael Hunold <hunold@linuxtv.org>

- tda1004x: standardised where the firmware should be.

- tda1004x: need to re-invert inversion for tda10046 in get_fe()

- tda1004x: reset chip before uploading firmware

- tda1004x: split firmware upload off from frontend init.  the initial tune
  attempt was taking too long.  provide explanation of tuner frequency
  calculations

- tda1004x: Fixed signal strength reading for tda10046h
parent 9820b419
/* /*
Driver for Philips tda1004xh OFDM Frontend Driver for Philips tda1004xh OFDM Frontend
(c) 2003, 2004 Andrew de Quincey & Robert Schlabbach
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
...@@ -20,7 +22,7 @@ ...@@ -20,7 +22,7 @@
/* /*
This driver needs a copy of the DLL "ttlcdacc.dll" from the Haupauge or Technotrend This driver needs a copy of the DLL "ttlcdacc.dll" from the Haupauge or Technotrend
windows driver saved as '/usr/lib/hotplug/firmware/tda1004x.mc'. windows driver saved as '/usr/lib/hotplug/firmware/tda1004x.bin'.
You can also pass the complete file name with the module parameter 'tda1004x_firmware'. You can also pass the complete file name with the module parameter 'tda1004x_firmware'.
Currently the DLL from v2.15a of the technotrend driver is supported. Other versions can Currently the DLL from v2.15a of the technotrend driver is supported. Other versions can
...@@ -46,16 +48,12 @@ ...@@ -46,16 +48,12 @@
#include "dvb_functions.h" #include "dvb_functions.h"
#ifndef DVB_TDA1004X_FIRMWARE_FILE #ifndef DVB_TDA1004X_FIRMWARE_FILE
#define DVB_TDA1004X_FIRMWARE_FILE "/usr/lib/hotplug/firmware/tda1004x.mc" #define DVB_TDA1004X_FIRMWARE_FILE "/usr/lib/hotplug/firmware/tda1004x.bin"
#endif #endif
static int tda1004x_debug = 0; static int tda1004x_debug = 0;
static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE; static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE;
#define TDA10045H_ADDRESS 0x08
#define TD1344_ADDRESS 0x61
#define TDM1316L_ADDRESS 0x63
#define MC44BC374_ADDRESS 0x65 #define MC44BC374_ADDRESS 0x65
#define TDA1004X_CHIPID 0x00 #define TDA1004X_CHIPID 0x00
...@@ -67,8 +65,8 @@ static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE; ...@@ -67,8 +65,8 @@ static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE;
#define TDA1004X_STATUS_CD 0x06 #define TDA1004X_STATUS_CD 0x06
#define TDA1004X_CONFC4 0x07 #define TDA1004X_CONFC4 0x07
#define TDA1004X_DSSPARE2 0x0C #define TDA1004X_DSSPARE2 0x0C
#define TDA1004X_CODE_IN 0x0D #define TDA10045H_CODE_IN 0x0D
#define TDA1004X_FWPAGE 0x0E #define TDA10045H_FWPAGE 0x0E
#define TDA1004X_SCAN_CPT 0x10 #define TDA1004X_SCAN_CPT 0x10
#define TDA1004X_DSP_CMD 0x11 #define TDA1004X_DSP_CMD 0x11
#define TDA1004X_DSP_ARG 0x12 #define TDA1004X_DSP_ARG 0x12
...@@ -76,10 +74,11 @@ static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE; ...@@ -76,10 +74,11 @@ static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE;
#define TDA1004X_DSP_DATA2 0x14 #define TDA1004X_DSP_DATA2 0x14
#define TDA1004X_CONFADC1 0x15 #define TDA1004X_CONFADC1 0x15
#define TDA1004X_CONFC1 0x16 #define TDA1004X_CONFC1 0x16
#define TDA1004X_SIGNAL_STRENGTH 0x1a #define TDA10045H_S_AGC 0x1a
#define TDA10046H_AGC_TUN_LEVEL 0x1a
#define TDA1004X_SNR 0x1c #define TDA1004X_SNR 0x1c
#define TDA1004X_REG1E 0x1e #define TDA1004X_CONF_TS1 0x1e
#define TDA1004X_REG1F 0x1f #define TDA1004X_CONF_TS2 0x1f
#define TDA1004X_CBER_RESET 0x20 #define TDA1004X_CBER_RESET 0x20
#define TDA1004X_CBER_MSB 0x21 #define TDA1004X_CBER_MSB 0x21
#define TDA1004X_CBER_LSB 0x22 #define TDA1004X_CBER_LSB 0x22
...@@ -88,18 +87,58 @@ static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE; ...@@ -88,18 +87,58 @@ static char *tda1004x_firmware = DVB_TDA1004X_FIRMWARE_FILE;
#define TDA1004X_VBER_MID 0x25 #define TDA1004X_VBER_MID 0x25
#define TDA1004X_VBER_LSB 0x26 #define TDA1004X_VBER_LSB 0x26
#define TDA1004X_UNCOR 0x27 #define TDA1004X_UNCOR 0x27
#define TDA1004X_CONFPLL_P 0x2D
#define TDA1004X_CONFPLL_M_MSB 0x2E #define TDA10045H_CONFPLL_P 0x2D
#define TDA1004X_CONFPLL_M_LSB 0x2F #define TDA10045H_CONFPLL_M_MSB 0x2E
#define TDA1004X_CONFPLL_N 0x30 #define TDA10045H_CONFPLL_M_LSB 0x2F
#define TDA1004X_UNSURW_MSB 0x31 #define TDA10045H_CONFPLL_N 0x30
#define TDA1004X_UNSURW_LSB 0x32
#define TDA1004X_WREF_MSB 0x33 #define TDA10046H_CONFPLL1 0x2D
#define TDA1004X_WREF_MID 0x34 #define TDA10046H_CONFPLL2 0x2F
#define TDA1004X_WREF_LSB 0x35 #define TDA10046H_CONFPLL3 0x30
#define TDA1004X_MUXOUT 0x36 #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 TDA1004X_CONFADC2 0x37
#define TDA1004X_IOFFSET 0x38
#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 0x58
#define FE_TYPE_TDA10045H 0
#define FE_TYPE_TDA10046H 1
#define TUNER_TYPE_TD1344 0
#define TUNER_TYPE_TD1316 1
#define dprintk if (tda1004x_debug) printk #define dprintk if (tda1004x_debug) printk
...@@ -116,11 +155,27 @@ static struct dvb_frontend_info tda10045h_info = { ...@@ -116,11 +155,27 @@ static struct dvb_frontend_info tda10045h_info = {
FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
}; };
static struct dvb_frontend_info tda10046h_info = {
.name = "Philips TDA10046H",
.type = FE_OFDM,
.frequency_min = 51000000,
.frequency_max = 858000000,
.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
};
#pragma pack(1) #pragma pack(1)
struct tda1004x_state { struct tda1004x_state {
u8 tda1004x_address; u8 tda1004x_address;
u8 tuner_address; u8 tuner_address;
u8 initialised:1; u8 initialised:1;
u8 tuner_type:2;
u8 fe_type:2;
}; };
#pragma pack() #pragma pack()
...@@ -132,6 +187,9 @@ struct fwinfo { ...@@ -132,6 +187,9 @@ struct fwinfo {
static struct fwinfo tda10045h_fwinfo[] = { {.file_size = 286720,.fw_offset = 0x34cc5,.fw_size = 30555} }; static struct fwinfo tda10045h_fwinfo[] = { {.file_size = 286720,.fw_offset = 0x34cc5,.fw_size = 30555} };
static int tda10045h_fwinfo_count = sizeof(tda10045h_fwinfo) / sizeof(struct fwinfo); static int tda10045h_fwinfo_count = sizeof(tda10045h_fwinfo) / sizeof(struct fwinfo);
static struct fwinfo tda10046h_fwinfo[] = { {.file_size = 286720,.fw_offset = 0x3c4f9,.fw_size = 24479} };
static int tda10046h_fwinfo_count = sizeof(tda10046h_fwinfo) / sizeof(struct fwinfo);
static int errno; static int errno;
...@@ -246,46 +304,98 @@ static int tda10045h_set_bandwidth(struct dvb_i2c_bus *i2c, ...@@ -246,46 +304,98 @@ static int tda10045h_set_bandwidth(struct dvb_i2c_bus *i2c,
switch (bandwidth) { switch (bandwidth) {
case BANDWIDTH_6_MHZ: case BANDWIDTH_6_MHZ:
tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14); tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14);
tda1004x_write_buf(i2c, tda_state, TDA1004X_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz));
break; break;
case BANDWIDTH_7_MHZ: case BANDWIDTH_7_MHZ:
tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x80); tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x80);
tda1004x_write_buf(i2c, tda_state, TDA1004X_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz));
break; break;
case BANDWIDTH_8_MHZ: case BANDWIDTH_8_MHZ:
tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14); tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14);
tda1004x_write_buf(i2c, tda_state, TDA1004X_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz));
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
tda1004x_write_byte(i2c, tda_state, TDA1004X_IOFFSET, 0); tda1004x_write_byte(i2c, tda_state, TDA10045H_IOFFSET, 0);
// done
return 0;
}
static int tda10046h_set_bandwidth(struct dvb_i2c_bus *i2c,
struct tda1004x_state *tda_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(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz));
tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0);
break;
case BANDWIDTH_7_MHZ:
tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz));
tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0);
break;
case BANDWIDTH_8_MHZ:
tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz));
tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0xFF);
break;
default:
return -EINVAL;
}
// done // done
return 0; return 0;
} }
static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state) static int tda1004x_fwupload(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state)
{ {
u8 fw_buf[65]; u8 fw_buf[65];
struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = fw_buf,.len = 0 }; struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = fw_buf,.len = 0 };
struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = 0,.len = 0 };
unsigned char *firmware = NULL; unsigned char *firmware = NULL;
int filesize; int filesize;
int fd; int fd;
int fwinfo_idx; int fwinfo_idx;
int fw_size = 0; int fw_size = 0;
int fw_pos; int fw_pos, fw_offset;
int tx_size; int tx_size;
static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
mm_segment_t fs = get_fs(); mm_segment_t fs = get_fs();
int dspCodeCounterReg=0, dspCodeInReg=0, dspVersion=0;
int fwInfoCount=0;
struct fwinfo* fwInfo = NULL;
unsigned long timeout;
// DSP parameters
switch(tda_state->fe_type) {
case FE_TYPE_TDA10045H:
dspCodeCounterReg = TDA10045H_FWPAGE;
dspCodeInReg = TDA10045H_CODE_IN;
dspVersion = 0x2c;
fwInfoCount = tda10045h_fwinfo_count;
fwInfo = tda10045h_fwinfo;
break;
dprintk("%s\n", __FUNCTION__); case FE_TYPE_TDA10046H:
dspCodeCounterReg = TDA10046H_CODE_CPT;
dspCodeInReg = TDA10046H_CODE_IN;
dspVersion = 0x20;
fwInfoCount = tda10046h_fwinfo_count;
fwInfo = tda10046h_fwinfo;
break;
}
// Load the firmware // Load the firmware
set_fs(get_ds()); set_fs(get_ds());
...@@ -303,17 +413,18 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta ...@@ -303,17 +413,18 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta
return -EIO; return -EIO;
} }
// find extraction parameters // find extraction parameters for firmware
for (fwinfo_idx = 0; fwinfo_idx < tda10045h_fwinfo_count; fwinfo_idx++) { for (fwinfo_idx = 0; fwinfo_idx < fwInfoCount; fwinfo_idx++) {
if (tda10045h_fwinfo[fwinfo_idx].file_size == filesize) if (fwInfo[fwinfo_idx].file_size == filesize)
break; break;
} }
if (fwinfo_idx >= tda10045h_fwinfo_count) { if (fwinfo_idx >= fwInfoCount) {
printk("%s: Unsupported firmware %s\n", __FUNCTION__, tda1004x_firmware); printk("%s: Unsupported firmware %s\n", __FUNCTION__, tda1004x_firmware);
sys_close(fd); sys_close(fd);
return -EIO; return -EIO;
} }
fw_size = tda10045h_fwinfo[fwinfo_idx].fw_size; fw_size = fwInfo[fwinfo_idx].fw_size;
fw_offset = fwInfo[fwinfo_idx].fw_offset;
// allocate buffer for it // allocate buffer for it
firmware = vmalloc(fw_size); firmware = vmalloc(fw_size);
...@@ -325,7 +436,7 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta ...@@ -325,7 +436,7 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta
} }
// read it! // read it!
lseek(fd, tda10045h_fwinfo[fwinfo_idx].fw_offset, 0); lseek(fd, fw_offset, 0);
if (read(fd, firmware, fw_size) != fw_size) { if (read(fd, firmware, fw_size) != fw_size) {
printk("%s: Failed to read firmware\n", __FUNCTION__); printk("%s: Failed to read firmware\n", __FUNCTION__);
vfree(firmware); vfree(firmware);
...@@ -335,39 +446,51 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta ...@@ -335,39 +446,51 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta
sys_close(fd); sys_close(fd);
set_fs(fs); set_fs(fs);
// Disable the MC44BC374C // set some valid bandwith parameters before uploading
tda1004x_enable_tuner_i2c(i2c, tda_state); switch(tda_state->fe_type) {
tuner_msg.addr = MC44BC374_ADDRESS; case FE_TYPE_TDA10045H:
tuner_msg.buf = disable_mc44BC374c; // reset chip
tuner_msg.len = sizeof(disable_mc44BC374c); tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8);
if (i2c->xfer(i2c, &tuner_msg, 1) != 1) { tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0);
i2c->xfer(i2c, &tuner_msg, 1); dvb_delay(10);
}
tda1004x_disable_tuner_i2c(i2c, tda_state);
// set some valid bandwith parameters // set parameters
switch(tda_state->tda1004x_address) {
case TDA10045H_ADDRESS:
tda10045h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ); tda10045h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ);
break; break;
case FE_TYPE_TDA10046H:
// reset chip
tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_TRISTATE1, 1, 0);
dvb_delay(10);
// set parameters
tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL2, 10);
tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL3, 0);
tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_OFFSET, 99);
tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_LSB, 0x2c);
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
break;
} }
dvb_delay(500);
// do the firmware upload // do the firmware upload
tda1004x_write_byte(i2c, tda_state, TDA1004X_FWPAGE, 0); tda1004x_write_byte(i2c, tda_state, dspCodeCounterReg, 0); // clear code counter
fw_msg.addr = tda_state->tda1004x_address; fw_msg.addr = tda_state->tda1004x_address;
fw_pos = 0; fw_pos = 0;
while (fw_pos != fw_size) { while (fw_pos != fw_size) {
// work out how much to send this time // work out how much to send this time
tx_size = fw_size - fw_pos; tx_size = fw_size - fw_pos;
if (tx_size > 64) { if (tx_size > 0x10) {
tx_size = 64; tx_size = 0x10;
} }
// send the chunk // send the chunk
fw_buf[0] = TDA1004X_CODE_IN; fw_buf[0] = dspCodeInReg;
memcpy(fw_buf + 1, firmware + fw_pos, tx_size); memcpy(fw_buf + 1, firmware + fw_pos, tx_size);
fw_msg.len = tx_size + 1; fw_msg.len = tx_size + 1;
if (i2c->xfer(i2c, &fw_msg, 1) != 1) { if (i2c->xfer(i2c, &fw_msg, 1) != 1) {
printk("tda1004x: Error during firmware upload\n");
vfree(firmware); vfree(firmware);
return -EIO; return -EIO;
} }
...@@ -375,35 +498,128 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta ...@@ -375,35 +498,128 @@ static int tda1004x_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_sta
dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, fw_pos); dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, fw_pos);
} }
dvb_delay(100);
vfree(firmware); vfree(firmware);
// Initialise the DSP and check upload was OK // wait for DSP to initialise
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x10, 0); switch(tda_state->fe_type) {
case FE_TYPE_TDA10045H:
// DSPREADY doesn't seem to work on the TDA10045H
dvb_delay(100);
break;
case FE_TYPE_TDA10046H:
timeout = jiffies + HZ;
while(!(tda1004x_read_byte(i2c, tda_state, TDA1004X_STATUS_CD) & 0x20)) {
if (time_after(jiffies, timeout)) {
printk("tda1004x: DSP failed to initialised.\n");
return -EIO;
}
dvb_delay(1);
}
break;
}
// check upload was OK
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
tda1004x_write_byte(i2c, tda_state, TDA1004X_DSP_CMD, 0x67); tda1004x_write_byte(i2c, tda_state, TDA1004X_DSP_CMD, 0x67);
if ((tda1004x_read_byte(i2c, tda_state, TDA1004X_DSP_DATA1) != 0x67) || if ((tda1004x_read_byte(i2c, tda_state, TDA1004X_DSP_DATA1) != 0x67) ||
(tda1004x_read_byte(i2c, tda_state, TDA1004X_DSP_DATA2) != 0x2c)) { (tda1004x_read_byte(i2c, tda_state, TDA1004X_DSP_DATA2) != dspVersion)) {
printk("%s: firmware upload failed!\n", __FUNCTION__); printk("%s: firmware upload failed!\n", __FUNCTION__);
return -EIO; return -EIO;
} }
// success
return 0;
}
static int tda10045h_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state)
{
struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = 0,.len = 0 };
static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
dprintk("%s\n", __FUNCTION__);
// Disable the MC44BC374C
tda1004x_enable_tuner_i2c(i2c, tda_state);
tuner_msg.addr = MC44BC374_ADDRESS;
tuner_msg.buf = disable_mc44BC374c;
tuner_msg.len = sizeof(disable_mc44BC374c);
if (i2c->xfer(i2c, &tuner_msg, 1) != 1) {
i2c->xfer(i2c, &tuner_msg, 1);
}
tda1004x_disable_tuner_i2c(i2c, tda_state);
// tda setup // tda setup
tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream
tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0x0); tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0); // no frequency inversion
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer
tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset
tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset
tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface
tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface
tda1004x_write_mask(i2c, tda_state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity
tda1004x_write_byte(i2c, tda_state, TDA1004X_CONFADC1, 0x2e); tda1004x_write_byte(i2c, tda_state, TDA1004X_CONFADC1, 0x2e);
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0x80);
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0);
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x10, 0);
tda1004x_write_byte(i2c, tda_state, TDA1004X_REG1E, 0);
tda1004x_write_byte(i2c, tda_state, TDA1004X_REG1F, 0);
tda1004x_write_mask(i2c, tda_state, TDA1004X_VBER_MSB, 0xe0, 0xa0);
// done // done
return 0; return 0;
} }
static int tda10046h_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_state)
{
struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = 0,.len = 0 };
static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
dprintk("%s\n", __FUNCTION__);
// Disable the MC44BC374C
tda1004x_enable_tuner_i2c(i2c, tda_state);
tuner_msg.addr = MC44BC374_ADDRESS;
tuner_msg.buf = disable_mc44BC374c;
tuner_msg.len = sizeof(disable_mc44BC374c);
if (i2c->xfer(i2c, &tuner_msg, 1) != 1) {
i2c->xfer(i2c, &tuner_msg, 1);
}
tda1004x_disable_tuner_i2c(i2c, tda_state);
// tda setup
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0x40); // TT TDA10046H needs inversion ON
tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer
tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL2, 10); // PLL M = 10
tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0
tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99
tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221
tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // }
tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_CONF, 0); // AGC setup
tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities
tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MIN, 0); // }
tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MIN, 0); // }
tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MAX, 0xff); // }
tda1004x_write_mask(i2c, tda_state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits
tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1
tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm
tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config
tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN
tda1004x_write_byte(i2c, tda_state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup
tda1004x_write_byte(i2c, tda_state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config
tda1004x_write_mask(i2c, tda_state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select
tda10046h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz
// done
return 0;
}
static int tda1004x_encode_fec(int fec) static int tda1004x_encode_fec(int fec)
{ {
// convert known FEC values // convert known FEC values
...@@ -450,17 +666,18 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, ...@@ -450,17 +666,18 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c,
{ {
u8 tuner_buf[4]; u8 tuner_buf[4];
struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) }; struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) };
int tuner_frequency; int tuner_frequency = 0;
u8 band, cp, filter; u8 band, cp, filter;
int counter, counter2; int counter, counter2;
dprintk("%s\n", __FUNCTION__); dprintk("%s\n", __FUNCTION__);
// setup the frequency buffer // setup the frequency buffer
switch (tda_state->tuner_address) { switch (tda_state->tuner_type) {
case TD1344_ADDRESS: case TUNER_TYPE_TD1344:
// setup tuner buffer // setup tuner buffer
// ((Fif+((1000000/6)/2)) + Finput)/(1000000/6)
tuner_frequency = tuner_frequency =
(((fe_params->frequency / 1000) * 6) + 217502) / 1000; (((fe_params->frequency / 1000) * 6) + 217502) / 1000;
tuner_buf[0] = tuner_frequency >> 8; tuner_buf[0] = tuner_frequency >> 8;
...@@ -499,7 +716,7 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, ...@@ -499,7 +716,7 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c,
tda1004x_disable_tuner_i2c(i2c, tda_state); tda1004x_disable_tuner_i2c(i2c, tda_state);
break; break;
case TDM1316L_ADDRESS: case TUNER_TYPE_TD1316:
// determine charge pump // determine charge pump
tuner_frequency = fe_params->frequency + 36130000; tuner_frequency = fe_params->frequency + 36130000;
if (tuner_frequency < 87000000) { if (tuner_frequency < 87000000) {
...@@ -542,9 +759,7 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, ...@@ -542,9 +759,7 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c,
// work out filter // work out filter
switch (fe_params->u.ofdm.bandwidth) { switch (fe_params->u.ofdm.bandwidth) {
case BANDWIDTH_6_MHZ: case BANDWIDTH_6_MHZ:
// 6 MHz isn't supported directly, but set this to filter = 0;
// the 8 MHz setting in case we can fiddle it later
filter = 1;
break; break;
case BANDWIDTH_7_MHZ: case BANDWIDTH_7_MHZ:
...@@ -559,15 +774,27 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, ...@@ -559,15 +774,27 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c,
return -EINVAL; return -EINVAL;
} }
// calculate tuner parameters // calculate divisor
// ((36130000+((1000000/6)/2)) + Finput)/(1000000/6)
tuner_frequency = tuner_frequency =
(((fe_params->frequency / 1000) * 6) + 217280) / 1000; (((fe_params->frequency / 1000) * 6) + 217280) / 1000;
// setup tuner buffer
tuner_buf[0] = tuner_frequency >> 8; tuner_buf[0] = tuner_frequency >> 8;
tuner_buf[1] = tuner_frequency & 0xff; tuner_buf[1] = tuner_frequency & 0xff;
tuner_buf[2] = 0xca; tuner_buf[2] = 0xca;
tuner_buf[3] = (cp << 5) | (filter << 3) | band; tuner_buf[3] = (cp << 5) | (filter << 3) | band;
// tune it // tune it
if (tda_state->fe_type == FE_TYPE_TDA10046H) {
// setup auto offset
tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10);
tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x80, 0);
tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0);
// disable agc_conf[2]
tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 0);
}
tda1004x_enable_tuner_i2c(i2c, tda_state); tda1004x_enable_tuner_i2c(i2c, tda_state);
tuner_msg.addr = tda_state->tuner_address; tuner_msg.addr = tda_state->tuner_address;
tuner_msg.len = 4; tuner_msg.len = 4;
...@@ -576,6 +803,8 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c, ...@@ -576,6 +803,8 @@ static int tda1004x_set_frequency(struct dvb_i2c_bus *i2c,
} }
dvb_delay(1); dvb_delay(1);
tda1004x_disable_tuner_i2c(i2c, tda_state); tda1004x_disable_tuner_i2c(i2c, tda_state);
if (tda_state->fe_type == FE_TYPE_TDA10046H)
tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 4);
break; break;
default: default:
...@@ -593,13 +822,12 @@ static int tda1004x_set_fe(struct dvb_i2c_bus *i2c, ...@@ -593,13 +822,12 @@ static int tda1004x_set_fe(struct dvb_i2c_bus *i2c,
struct dvb_frontend_parameters *fe_params) struct dvb_frontend_parameters *fe_params)
{ {
int tmp; int tmp;
int inversion;
dprintk("%s\n", __FUNCTION__); dprintk("%s\n", __FUNCTION__);
// set frequency // set frequency
tmp = tda1004x_set_frequency(i2c, tda_state, fe_params); if ((tmp = tda1004x_set_frequency(i2c, tda_state, fe_params)) < 0)
if (tmp < 0)
return tmp; return tmp;
// hardcoded to use auto as much as possible // hardcoded to use auto as much as possible
...@@ -673,14 +901,24 @@ static int tda1004x_set_fe(struct dvb_i2c_bus *i2c, ...@@ -673,14 +901,24 @@ static int tda1004x_set_fe(struct dvb_i2c_bus *i2c,
} }
// set bandwidth // set bandwidth
switch(tda_state->tda1004x_address) { switch(tda_state->fe_type) {
case TDA10045H_ADDRESS: case FE_TYPE_TDA10045H:
tda10045h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth); tda10045h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth);
break; break;
case FE_TYPE_TDA10046H:
tda10046h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth);
break;
}
// need to invert the inversion for TT TDA10046H
inversion = fe_params->inversion;
if (tda_state->fe_type == FE_TYPE_TDA10046H) {
inversion = inversion ? INVERSION_OFF : INVERSION_ON;
} }
// set inversion // set inversion
switch (fe_params->inversion) { switch (inversion) {
case INVERSION_OFF: case INVERSION_OFF:
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0); tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0);
break; break;
...@@ -745,10 +983,19 @@ static int tda1004x_set_fe(struct dvb_i2c_bus *i2c, ...@@ -745,10 +983,19 @@ static int tda1004x_set_fe(struct dvb_i2c_bus *i2c,
return -EINVAL; return -EINVAL;
} }
// reset DSP // start the lock
switch(tda_state->fe_type) {
case FE_TYPE_TDA10045H:
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8); tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8);
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0); tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0);
dvb_delay(10); dvb_delay(10);
break;
case FE_TYPE_TDA10046H:
tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x40, 0x40);
dvb_delay(10);
break;
}
// done // done
return 0; return 0;
...@@ -766,8 +1013,15 @@ static int tda1004x_get_fe(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_s ...@@ -766,8 +1013,15 @@ static int tda1004x_get_fe(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_s
fe_params->inversion = INVERSION_ON; fe_params->inversion = INVERSION_ON;
} }
// need to invert the inversion for TT TDA10046H
if (tda_state->fe_type == FE_TYPE_TDA10046H) {
fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON;
}
// bandwidth // bandwidth
switch (tda1004x_read_byte(i2c, tda_state, TDA1004X_WREF_LSB)) { switch(tda_state->fe_type) {
case FE_TYPE_TDA10045H:
switch (tda1004x_read_byte(i2c, tda_state, TDA10045H_WREF_LSB)) {
case 0x14: case 0x14:
fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
break; break;
...@@ -778,6 +1032,22 @@ static int tda1004x_get_fe(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_s ...@@ -778,6 +1032,22 @@ static int tda1004x_get_fe(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_s
fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
break; break;
} }
break;
case FE_TYPE_TDA10046H:
switch (tda1004x_read_byte(i2c, tda_state, 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;
}
break;
}
// FEC // FEC
fe_params->u.ofdm.code_rate_HP = fe_params->u.ofdm.code_rate_HP =
...@@ -906,11 +1176,23 @@ static int tda1004x_read_status(struct dvb_i2c_bus *i2c, struct tda1004x_state* ...@@ -906,11 +1176,23 @@ static int tda1004x_read_status(struct dvb_i2c_bus *i2c, struct tda1004x_state*
static int tda1004x_read_signal_strength(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, u16 * signal) static int tda1004x_read_signal_strength(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, u16 * signal)
{ {
int tmp; int tmp;
int reg = 0;
dprintk("%s\n", __FUNCTION__); dprintk("%s\n", __FUNCTION__);
// determine the register to use
switch(tda_state->fe_type) {
case FE_TYPE_TDA10045H:
reg = TDA10045H_S_AGC;
break;
case FE_TYPE_TDA10046H:
reg = TDA10046H_AGC_IF_LEVEL;
break;
}
// read it // read it
tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_SIGNAL_STRENGTH); tmp = tda1004x_read_byte(i2c, tda_state, reg);
if (tmp < 0) if (tmp < 0)
return -EIO; return -EIO;
...@@ -1009,10 +1291,14 @@ static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) ...@@ -1009,10 +1291,14 @@ static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
switch (cmd) { switch (cmd) {
case FE_GET_INFO: case FE_GET_INFO:
switch(tda_state->tda1004x_address) { switch(tda_state->fe_type) {
case TDA10045H_ADDRESS: case FE_TYPE_TDA10045H:
memcpy(arg, &tda10045h_info, sizeof(struct dvb_frontend_info)); memcpy(arg, &tda10045h_info, sizeof(struct dvb_frontend_info));
break; break;
case FE_TYPE_TDA10046H:
memcpy(arg, &tda10046h_info, sizeof(struct dvb_frontend_info));
break;
} }
break; break;
...@@ -1043,7 +1329,15 @@ static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) ...@@ -1043,7 +1329,15 @@ static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
return 0; return 0;
// OK, perform initialisation // OK, perform initialisation
status = tda1004x_init(i2c, tda_state); switch(tda_state->fe_type) {
case FE_TYPE_TDA10045H:
status = tda10045h_init(i2c, tda_state);
break;
case FE_TYPE_TDA10046H:
status = tda10046h_init(i2c, tda_state);
break;
}
if (status == 0) if (status == 0)
tda_state->initialised = 1; tda_state->initialised = 1;
return status; return status;
...@@ -1060,42 +1354,81 @@ static int tda1004x_attach(struct dvb_i2c_bus *i2c, void **data) ...@@ -1060,42 +1354,81 @@ static int tda1004x_attach(struct dvb_i2c_bus *i2c, void **data)
{ {
int tda1004x_address = -1; int tda1004x_address = -1;
int tuner_address = -1; int tuner_address = -1;
int fe_type = -1;
int tuner_type = -1;
struct tda1004x_state tda_state; struct tda1004x_state tda_state;
struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=0, .len=0 }; struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=0, .len=0 };
static u8 td1344_init[] = { 0x0b, 0xf5, 0x88, 0xab }; static u8 td1344_init[] = { 0x0b, 0xf5, 0x88, 0xab };
static u8 tdm1316l_init[] = { 0x0b, 0xf5, 0x85, 0xab }; static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
static u8 td1316_init_tda10046h[] = { 0x0b, 0xf5, 0x80, 0xab };
int status;
dprintk("%s\n", __FUNCTION__); dprintk("%s\n", __FUNCTION__);
// probe for frontend // probe for tda10045h
tda_state.tda1004x_address = TDA10045H_ADDRESS; if (tda1004x_address == -1) {
tda_state.tda1004x_address = 0x08;
if (tda1004x_read_byte(i2c, &tda_state, TDA1004X_CHIPID) == 0x25) { if (tda1004x_read_byte(i2c, &tda_state, TDA1004X_CHIPID) == 0x25) {
tda1004x_address = TDA10045H_ADDRESS; tda1004x_address = 0x08;
fe_type = FE_TYPE_TDA10045H;
printk("tda1004x: Detected Philips TDA10045H.\n"); printk("tda1004x: Detected Philips TDA10045H.\n");
} }
}
// probe for tda10046h
if (tda1004x_address == -1) {
tda_state.tda1004x_address = 0x08;
if (tda1004x_read_byte(i2c, &tda_state, TDA1004X_CHIPID) == 0x46) {
tda1004x_address = 0x08;
fe_type = FE_TYPE_TDA10046H;
printk("tda1004x: Detected Philips TDA10046H.\n");
}
}
// did we find a frontend? // did we find a frontend?
if (tda1004x_address == -1) { if (tda1004x_address == -1) {
return -ENODEV; return -ENODEV;
} }
// supported tuner? // enable access to the tuner
tda1004x_enable_tuner_i2c(i2c, &tda_state); tda1004x_enable_tuner_i2c(i2c, &tda_state);
tuner_msg.addr = TD1344_ADDRESS;
// check for a TD1344 first
if (tuner_address == -1) {
tuner_msg.addr = 0x61;
tuner_msg.buf = td1344_init; tuner_msg.buf = td1344_init;
tuner_msg.len = sizeof(td1344_init); tuner_msg.len = sizeof(td1344_init);
if (i2c->xfer(i2c, &tuner_msg, 1) == 1) { if (i2c->xfer(i2c, &tuner_msg, 1) == 1) {
dvb_delay(1); dvb_delay(1);
tuner_address = TD1344_ADDRESS; tuner_address = 0x61;
printk("tda1004x: Detected Philips TD1344 tuner. PLEASE CHECK THIS AND REPORT BACK!.\n"); tuner_type = TUNER_TYPE_TD1344;
} else { printk("tda1004x: Detected Philips TD1344 tuner.\n");
tuner_msg.addr = TDM1316L_ADDRESS; }
tuner_msg.buf = tdm1316l_init; }
tuner_msg.len = sizeof(tdm1316l_init);
// OK, try a TD1316 on address 0x63
if (tuner_address == -1) {
tuner_msg.addr = 0x63;
tuner_msg.buf = td1316_init;
tuner_msg.len = sizeof(td1316_init);
if (i2c->xfer(i2c, &tuner_msg, 1) == 1) { if (i2c->xfer(i2c, &tuner_msg, 1) == 1) {
dvb_delay(1); dvb_delay(1);
tuner_address = TDM1316L_ADDRESS; tuner_address = 0x63;
printk("tda1004x: Detected Philips TDM1316L tuner.\n"); tuner_type = TUNER_TYPE_TD1316;
printk("tda1004x: Detected Philips TD1316 tuner.\n");
}
}
// OK, TD1316 again, on address 0x60 (TDA10046H)
if (tuner_address == -1) {
tuner_msg.addr = 0x60;
tuner_msg.buf = td1316_init_tda10046h;
tuner_msg.len = sizeof(td1316_init_tda10046h);
if (i2c->xfer(i2c, &tuner_msg, 1) == 1) {
dvb_delay(1);
tuner_address = 0x60;
tuner_type = TUNER_TYPE_TD1316;
printk("tda1004x: Detected Philips TD1316 tuner.\n");
} }
} }
tda1004x_disable_tuner_i2c(i2c, &tda_state); tda1004x_disable_tuner_i2c(i2c, &tda_state);
...@@ -1108,16 +1441,25 @@ static int tda1004x_attach(struct dvb_i2c_bus *i2c, void **data) ...@@ -1108,16 +1441,25 @@ static int tda1004x_attach(struct dvb_i2c_bus *i2c, void **data)
// create state // create state
tda_state.tda1004x_address = tda1004x_address; tda_state.tda1004x_address = tda1004x_address;
tda_state.fe_type = fe_type;
tda_state.tuner_address = tuner_address; tda_state.tuner_address = tuner_address;
tda_state.tuner_type = tuner_type;
tda_state.initialised = 0; tda_state.initialised = 0;
// upload firmware
if ((status = tda1004x_fwupload(i2c, &tda_state)) != 0) return status;
// register // register
switch(tda_state.tda1004x_address) { switch(tda_state.fe_type) {
case TDA10045H_ADDRESS: case FE_TYPE_TDA10045H:
return dvb_register_frontend(tda1004x_ioctl, i2c, (void *)(*((u32*) &tda_state)), &tda10045h_info); return dvb_register_frontend(tda1004x_ioctl, i2c, (void *)(*((u32*) &tda_state)), &tda10045h_info);
default:
return -ENODEV; case FE_TYPE_TDA10046H:
return dvb_register_frontend(tda1004x_ioctl, i2c, (void *)(*((u32*) &tda_state)), &tda10046h_info);
} }
// should not get here
return -EINVAL;
} }
...@@ -1146,7 +1488,7 @@ void __exit exit_tda1004x(void) ...@@ -1146,7 +1488,7 @@ void __exit exit_tda1004x(void)
module_init(init_tda1004x); module_init(init_tda1004x);
module_exit(exit_tda1004x); module_exit(exit_tda1004x);
MODULE_DESCRIPTION("Philips TDA10045H DVB-T Frontend"); MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Frontend");
MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach"); MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment