From: Michael Hunold <hunold@linuxtv.org>
To: torvalds@osdl.org, akpm@osdl.org, linux-kernel@vger.kernel.org,
hunold@linuxtv.org
Subject: [PATCH 7/9] tda1004x DVB frontend update
Date: Mon, 23 Feb 2004 16:05:01 -0500 [thread overview]
Message-ID: <10775702843054@convergence.de> (raw)
In-Reply-To: <10775702831806@convergence.de>
- [DVB] tda1004x: standardised where the firmware should be.
- [DVB] tda1004x: need to re-invert inversion for tda10046 in get_fe()
- [DVB] tda1004x: reset chip before uploading firmware
- [DVB] tda1004x: split firmware upload off from frontend init. the initial tune attempt was taking too long. provide explanation of tuner frequency calculations
- [DVB] tda1004x: Fixed signal strength reading for tda10046h
diff -uNrwB --new-file xx-linux-2.6.3/drivers/media/dvb/frontends/tda1004x.c linux-2.6.3.p/drivers/media/dvb/frontends/tda1004x.c
--- xx-linux-2.6.3/drivers/media/dvb/frontends/tda1004x.c 2004-01-09 09:22:40.000000000 +0100
+++ linux-2.6.3.p/drivers/media/dvb/frontends/tda1004x.c 2004-02-02 19:28:30.000000000 +0100
@@ -1,6 +1,8 @@
/*
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
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
@@ -20,7 +22,7 @@
/*
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'.
Currently the DLL from v2.15a of the technotrend driver is supported. Other versions can
@@ -45,16 +47,12 @@
#include "dvb_functions.h"
#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
static int tda1004x_debug = 0;
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 TDA1004X_CHIPID 0x00
@@ -66,8 +64,8 @@
#define TDA1004X_STATUS_CD 0x06
#define TDA1004X_CONFC4 0x07
#define TDA1004X_DSSPARE2 0x0C
-#define TDA1004X_CODE_IN 0x0D
-#define TDA1004X_FWPAGE 0x0E
+#define TDA10045H_CODE_IN 0x0D
+#define TDA10045H_FWPAGE 0x0E
#define TDA1004X_SCAN_CPT 0x10
#define TDA1004X_DSP_CMD 0x11
#define TDA1004X_DSP_ARG 0x12
@@ -75,10 +73,11 @@
#define TDA1004X_DSP_DATA2 0x14
#define TDA1004X_CONFADC1 0x15
#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_REG1E 0x1e
-#define TDA1004X_REG1F 0x1f
+#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
@@ -87,18 +86,58 @@
#define TDA1004X_VBER_MID 0x25
#define TDA1004X_VBER_LSB 0x26
#define TDA1004X_UNCOR 0x27
-#define TDA1004X_CONFPLL_P 0x2D
-#define TDA1004X_CONFPLL_M_MSB 0x2E
-#define TDA1004X_CONFPLL_M_LSB 0x2F
-#define TDA1004X_CONFPLL_N 0x30
-#define TDA1004X_UNSURW_MSB 0x31
-#define TDA1004X_UNSURW_LSB 0x32
-#define TDA1004X_WREF_MSB 0x33
-#define TDA1004X_WREF_MID 0x34
-#define TDA1004X_WREF_LSB 0x35
-#define TDA1004X_MUXOUT 0x36
+
+#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 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
@@ -115,11 +154,27 @@
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)
struct tda1004x_state {
u8 tda1004x_address;
u8 tuner_address;
u8 initialised:1;
+ u8 tuner_type:2;
+ u8 fe_type:2;
};
#pragma pack()
@@ -131,6 +186,9 @@
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 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;
@@ -245,46 +302,98 @@
switch (bandwidth) {
case BANDWIDTH_6_MHZ:
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;
case BANDWIDTH_7_MHZ:
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;
case BANDWIDTH_8_MHZ:
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;
default:
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
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];
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;
int filesize;
int fd;
int fwinfo_idx;
int fw_size = 0;
- int fw_pos;
+ int fw_pos, fw_offset;
int tx_size;
- static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
mm_segment_t fs = get_fs();
-
- dprintk("%s\n", __FUNCTION__);
+ 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;
+
+ 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
set_fs(get_ds());
@@ -302,17 +411,18 @@
return -EIO;
}
- // find extraction parameters
- for (fwinfo_idx = 0; fwinfo_idx < tda10045h_fwinfo_count; fwinfo_idx++) {
- if (tda10045h_fwinfo[fwinfo_idx].file_size == filesize)
+ // find extraction parameters for firmware
+ for (fwinfo_idx = 0; fwinfo_idx < fwInfoCount; fwinfo_idx++) {
+ if (fwInfo[fwinfo_idx].file_size == filesize)
break;
}
- if (fwinfo_idx >= tda10045h_fwinfo_count) {
+ if (fwinfo_idx >= fwInfoCount) {
printk("%s: Unsupported firmware %s\n", __FUNCTION__, tda1004x_firmware);
sys_close(fd);
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
firmware = vmalloc(fw_size);
@@ -324,7 +434,7 @@
}
// read it!
- lseek(fd, tda10045h_fwinfo[fwinfo_idx].fw_offset, 0);
+ lseek(fd, fw_offset, 0);
if (read(fd, firmware, fw_size) != fw_size) {
printk("%s: Failed to read firmware\n", __FUNCTION__);
vfree(firmware);
@@ -334,26 +444,35 @@
sys_close(fd);
set_fs(fs);
- // 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);
+ // set some valid bandwith parameters before uploading
+ switch(tda_state->fe_type) {
+ case FE_TYPE_TDA10045H:
+ // reset chip
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8);
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0);
+ dvb_delay(10);
- // set some valid bandwith parameters
- switch(tda_state->tda1004x_address) {
- case TDA10045H_ADDRESS:
+ // set parameters
tda10045h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ);
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
- 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_pos = 0;
while (fw_pos != fw_size) {
@@ -357,16 +476,19 @@
fw_msg.addr = tda_state->tda1004x_address;
fw_pos = 0;
while (fw_pos != fw_size) {
+
// work out how much to send this time
tx_size = fw_size - fw_pos;
- if (tx_size > 64) {
- tx_size = 64;
+ if (tx_size > 0x10) {
+ tx_size = 0x10;
}
+
// send the chunk
- fw_buf[0] = TDA1004X_CODE_IN;
+ fw_buf[0] = dspCodeInReg;
memcpy(fw_buf + 1, firmware + fw_pos, tx_size);
fw_msg.len = tx_size + 1;
if (i2c->xfer(i2c, &fw_msg, 1) != 1) {
+ printk("tda1004x: Error during firmware upload\n");
vfree(firmware);
return -EIO;
}
@@ -374,35 +496,128 @@
dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, fw_pos);
}
- dvb_delay(100);
vfree(firmware);
- // Initialise the DSP and check upload was OK
- tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x10, 0);
+ // wait for DSP to initialise
+ 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);
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__);
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
- tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0);
- tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10);
- tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0x0);
- tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0);
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0); // no frequency inversion
+ 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_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
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)
{
// convert known FEC values
@@ -449,17 +664,18 @@
{
u8 tuner_buf[4];
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;
int counter, counter2;
dprintk("%s\n", __FUNCTION__);
// setup the frequency buffer
- switch (tda_state->tuner_address) {
- case TD1344_ADDRESS:
+ switch (tda_state->tuner_type) {
+ case TUNER_TYPE_TD1344:
// setup tuner buffer
+ // ((Fif+((1000000/6)/2)) + Finput)/(1000000/6)
tuner_frequency =
(((fe_params->frequency / 1000) * 6) + 217502) / 1000;
tuner_buf[0] = tuner_frequency >> 8;
@@ -498,7 +714,7 @@
tda1004x_disable_tuner_i2c(i2c, tda_state);
break;
- case TDM1316L_ADDRESS:
+ case TUNER_TYPE_TD1316:
// determine charge pump
tuner_frequency = fe_params->frequency + 36130000;
if (tuner_frequency < 87000000) {
@@ -541,9 +757,7 @@
// work out filter
switch (fe_params->u.ofdm.bandwidth) {
case BANDWIDTH_6_MHZ:
- // 6 MHz isn't supported directly, but set this to
- // the 8 MHz setting in case we can fiddle it later
- filter = 1;
+ filter = 0;
break;
case BANDWIDTH_7_MHZ:
@@ -558,15 +772,27 @@
return -EINVAL;
}
- // calculate tuner parameters
+ // calculate divisor
+ // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6)
tuner_frequency =
(((fe_params->frequency / 1000) * 6) + 217280) / 1000;
+
+ // setup tuner buffer
tuner_buf[0] = tuner_frequency >> 8;
tuner_buf[1] = tuner_frequency & 0xff;
tuner_buf[2] = 0xca;
tuner_buf[3] = (cp << 5) | (filter << 3) | band;
// 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);
tuner_msg.addr = tda_state->tuner_address;
tuner_msg.len = 4;
@@ -575,6 +801,8 @@
}
dvb_delay(1);
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;
default:
@@ -592,6 +820,7 @@
struct dvb_frontend_parameters *fe_params)
{
int tmp;
+ int inversion;
dprintk("%s\n", __FUNCTION__);
@@ -595,10 +824,8 @@
dprintk("%s\n", __FUNCTION__);
-
// set frequency
- tmp = tda1004x_set_frequency(i2c, tda_state, fe_params);
- if (tmp < 0)
+ if ((tmp = tda1004x_set_frequency(i2c, tda_state, fe_params)) < 0)
return tmp;
// hardcoded to use auto as much as possible
@@ -672,14 +899,24 @@
}
// set bandwidth
- switch(tda_state->tda1004x_address) {
- case TDA10045H_ADDRESS:
+ switch(tda_state->fe_type) {
+ case FE_TYPE_TDA10045H:
tda10045h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth);
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
- switch (fe_params->inversion) {
+ switch (inversion) {
case INVERSION_OFF:
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0);
break;
@@ -744,10 +981,19 @@
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, 0);
dvb_delay(10);
+ break;
+
+ case FE_TYPE_TDA10046H:
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x40, 0x40);
+ dvb_delay(10);
+ break;
+ }
// done
return 0;
@@ -765,8 +1010,15 @@
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
- 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:
fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
break;
@@ -777,6 +1029,22 @@
fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
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
fe_params->u.ofdm.code_rate_HP =
@@ -905,11 +1173,23 @@
static int tda1004x_read_signal_strength(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state, u16 * signal)
{
int tmp;
+ int reg = 0;
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
- tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_SIGNAL_STRENGTH);
+ tmp = tda1004x_read_byte(i2c, tda_state, reg);
if (tmp < 0)
return -EIO;
@@ -1008,10 +1288,14 @@
switch (cmd) {
case FE_GET_INFO:
- switch(tda_state->tda1004x_address) {
- case TDA10045H_ADDRESS:
+ switch(tda_state->fe_type) {
+ case FE_TYPE_TDA10045H:
memcpy(arg, &tda10045h_info, sizeof(struct dvb_frontend_info));
break;
+
+ case FE_TYPE_TDA10046H:
+ memcpy(arg, &tda10046h_info, sizeof(struct dvb_frontend_info));
+ break;
}
break;
@@ -1042,7 +1327,15 @@
return 0;
// 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)
tda_state->initialised = 1;
return status;
@@ -1059,42 +1352,81 @@
{
int tda1004x_address = -1;
int tuner_address = -1;
+ int fe_type = -1;
+ int tuner_type = -1;
struct tda1004x_state tda_state;
struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=0, .len=0 };
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__);
- // probe for frontend
- tda_state.tda1004x_address = TDA10045H_ADDRESS;
+ // probe for tda10045h
+ if (tda1004x_address == -1) {
+ tda_state.tda1004x_address = 0x08;
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");
}
+ }
+
+ // 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?
if (tda1004x_address == -1) {
return -ENODEV;
}
- // supported tuner?
+ // enable access to the tuner
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.len = sizeof(td1344_init);
if (i2c->xfer(i2c, &tuner_msg, 1) == 1) {
dvb_delay(1);
- tuner_address = TD1344_ADDRESS;
- printk("tda1004x: Detected Philips TD1344 tuner. PLEASE CHECK THIS AND REPORT BACK!.\n");
- } else {
- tuner_msg.addr = TDM1316L_ADDRESS;
- tuner_msg.buf = tdm1316l_init;
- tuner_msg.len = sizeof(tdm1316l_init);
+ tuner_address = 0x61;
+ tuner_type = TUNER_TYPE_TD1344;
+ printk("tda1004x: Detected Philips TD1344 tuner.\n");
+ }
+ }
+
+ // 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) {
dvb_delay(1);
- tuner_address = TDM1316L_ADDRESS;
- printk("tda1004x: Detected Philips TDM1316L tuner.\n");
+ tuner_address = 0x63;
+ 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);
@@ -1107,16 +1439,25 @@
// create state
tda_state.tda1004x_address = tda1004x_address;
+ tda_state.fe_type = fe_type;
tda_state.tuner_address = tuner_address;
+ tda_state.tuner_type = tuner_type;
tda_state.initialised = 0;
+ // upload firmware
+ if ((status = tda1004x_fwupload(i2c, &tda_state)) != 0) return status;
+
// register
- switch(tda_state.tda1004x_address) {
- case TDA10045H_ADDRESS:
+ switch(tda_state.fe_type) {
+ case FE_TYPE_TDA10045H:
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;
}
@@ -1145,7 +1486,7 @@
module_init(init_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_LICENSE("GPL");
next prev parent reply other threads:[~2004-02-23 21:11 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-02-23 21:04 [PATCH 0/9] LinuxTV.org DVB update Michael Hunold
2004-02-23 21:04 ` [PATCH 1/9] Update the DVB subsystem docs Michael Hunold
2004-02-23 21:04 ` [PATCH 2/9] Update saa7146 driver core Michael Hunold
2004-02-23 21:04 ` [PATCH 3/9] Minor DVB Skystar2 updates Michael Hunold
2004-02-23 21:04 ` [PATCH 4/9] DVB core update Michael Hunold
2004-02-23 21:04 ` [PATCH 5/9] Misc. DVB frontend updates Michael Hunold
2004-02-23 21:05 ` [PATCH 6/9] stv0299 DVB frontend update Michael Hunold
2004-02-23 21:05 ` Michael Hunold [this message]
2004-02-23 21:05 ` [PATCH 8/9] av7110 DVB driver update Michael Hunold
2004-02-23 21:05 ` [PATCH 9/9] TTUSB-Budget " Michael Hunold
2004-02-23 21:18 ` [PATCH 7/9] tda1004x DVB frontend update Christoph Hellwig
2004-02-23 22:47 ` Michael Hunold
2004-02-23 22:54 ` Christoph Hellwig
2004-02-23 23:01 ` Michael Hunold
2004-02-23 22:09 ` Andrew Morton
2004-02-23 22:52 ` Michael Hunold
2004-02-23 23:11 ` Andrew Morton
2004-02-23 23:15 ` Ralph Metzler
2004-02-24 11:34 ` Marcel Holtmann
2004-02-23 22:23 ` [PATCH 1/9] Update the DVB subsystem docs Richard B. Johnson
2004-02-23 22:57 ` Michael Hunold
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=10775702843054@convergence.de \
--to=hunold@linuxtv.org \
--cc=akpm@osdl.org \
--cc=linux-kernel@vger.kernel.org \
--cc=torvalds@osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.