[linux-dvb] How to compile saa7134-dvb

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi, Hermann

> i^ve found your patches at http://www.linuxtv.org/pipermail/linux-dvb/2005-May.txt.gz, 
> i was able to apply only the first part of it( i^ve used this release like u told 
> http://dl.bytesex.org/cvs-snapshots/video4linux-20050426-090128.tar.gz). The second part 
> gived me tons of failed Hunks, beginnig with Makefile at 40 and so on... Its very wierd, 
> perhaps the patch itselfs is broken, perhaps my head is broked after sitting already 9 hours 
> at my pc. What am i doin wrong? Could you post all three parts attached as files? Would be very nice.
> 
> thx for advance

No, i originally was my fault. I sent the patches twice since i broke the first attempt.
I guess you got the first one.

I attached the patches again. 2 Notes:
- you will need to do some handwork if you try to compile for 2.6.12
- make install probably does not install the modules correctly, First try the load_modules
   script.

Good luck
   Hartmut
-------------- next part --------------
diff -uprNw video4linux.orig/tda1004x.c video4linux/tda1004x.c
--- video4linux.orig/tda1004x.c	1970-01-01 01:00:00.000000000 +0100
+++ video4linux/tda1004x.c	2005-04-05 00:37:05.000000000 +0200
@@ -0,0 +1,1251 @@
+  /*
+     Driver for Philips tda1004xh OFDM Demodulator
+
+     (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach
+	 
+	 modified 3/2005 by Hartmut Hackmann
+	 - tda10046: support 16MHz crystal
+	 - support boot from eeprom
+	 - support parameters for IF frequency and setup
+	 - be more relaxed with the firmware revision
+	 - rearranged initialization code to overcome stability problems
+	 
+
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+     (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+     GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with this program; if not, write to the Free Software
+     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   */
+/*
+ * This driver needs external firmware. Please use the commands
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ * download/extract them, and then copy them to /usr/lib/hotplug/firmware.
+ */
+#define TDA10045_DEFAULT_FIRMWARE "dvb-fe-tda10045.fw"
+#define TDA10046_DEFAULT_FIRMWARE "dvb-fe-tda10046.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include "dvb_frontend.h"
+#include "tda1004x.h"
+
+#define TDA1004X_DEMOD_TDA10045 0
+#define TDA1004X_DEMOD_TDA10046 1
+
+
+struct tda1004x_state
+{
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	const struct tda1004x_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* private demod data */
+	u8 initialised:1;
+
+	u8 demod_type;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "tda1004x: " args); \
+	} while (0)
+
+#define TDA1004X_CHIPID		 0x00
+#define TDA1004X_AUTO		 0x01
+#define TDA1004X_IN_CONF1	 0x02
+#define TDA1004X_IN_CONF2	 0x03
+#define TDA1004X_OUT_CONF1	 0x04
+#define TDA1004X_OUT_CONF2	 0x05
+#define TDA1004X_STATUS_CD	 0x06
+#define TDA1004X_CONFC4		 0x07
+#define TDA1004X_DSSPARE2	 0x0C
+#define TDA10045H_CODE_IN	 0x0D
+#define TDA10045H_FWPAGE	 0x0E
+#define TDA1004X_SCAN_CPT	 0x10
+#define TDA1004X_DSP_CMD	 0x11
+#define TDA1004X_DSP_ARG	 0x12
+#define TDA1004X_DSP_DATA1	 0x13
+#define TDA1004X_DSP_DATA2	 0x14
+#define TDA1004X_CONFADC1	 0x15
+#define TDA1004X_CONFC1		 0x16
+#define TDA10045H_S_AGC		 0x1a
+#define TDA10046H_AGC_TUN_LEVEL	 0x1a
+#define TDA1004X_SNR		 0x1c
+#define TDA1004X_CONF_TS1	 0x1e
+#define TDA1004X_CONF_TS2	 0x1f
+#define TDA1004X_CBER_RESET	 0x20
+#define TDA1004X_CBER_MSB	 0x21
+#define TDA1004X_CBER_LSB	 0x22
+#define TDA1004X_CVBER_LUT	 0x23
+#define TDA1004X_VBER_MSB	 0x24
+#define TDA1004X_VBER_MID	 0x25
+#define TDA1004X_VBER_LSB	 0x26
+#define TDA1004X_UNCOR		 0x27
+
+#define TDA10045H_CONFPLL_P	 0x2D
+#define TDA10045H_CONFPLL_M_MSB	 0x2E
+#define TDA10045H_CONFPLL_M_LSB	 0x2F
+#define TDA10045H_CONFPLL_N	 0x30
+
+#define TDA10046H_CONFPLL1	 0x2D
+#define TDA10046H_CONFPLL2	 0x2F
+#define TDA10046H_CONFPLL3	 0x30
+#define TDA10046H_TIME_WREF1	 0x31
+#define TDA10046H_TIME_WREF2	 0x32
+#define TDA10046H_TIME_WREF3	 0x33
+#define TDA10046H_TIME_WREF4	 0x34
+#define TDA10046H_TIME_WREF5	 0x35
+
+#define TDA10045H_UNSURW_MSB	 0x31
+#define TDA10045H_UNSURW_LSB	 0x32
+#define TDA10045H_WREF_MSB	 0x33
+#define TDA10045H_WREF_MID	 0x34
+#define TDA10045H_WREF_LSB	 0x35
+#define TDA10045H_MUXOUT	 0x36
+#define TDA1004X_CONFADC2	 0x37
+
+#define TDA10045H_IOFFSET	 0x38
+
+#define TDA10046H_CONF_TRISTATE1 0x3B
+#define TDA10046H_CONF_TRISTATE2 0x3C
+#define TDA10046H_CONF_POLARITY	 0x3D
+#define TDA10046H_FREQ_OFFSET	 0x3E
+#define TDA10046H_GPIO_OUT_SEL	 0x41
+#define TDA10046H_GPIO_SELECT	 0x42
+#define TDA10046H_AGC_CONF	 0x43
+#define TDA10046H_AGC_GAINS	 0x46
+#define TDA10046H_AGC_TUN_MIN	 0x47
+#define TDA10046H_AGC_TUN_MAX	 0x48
+#define TDA10046H_AGC_IF_MIN	 0x49
+#define TDA10046H_AGC_IF_MAX	 0x4A
+
+#define TDA10046H_FREQ_PHY2_MSB	 0x4D
+#define TDA10046H_FREQ_PHY2_LSB	 0x4E
+
+#define TDA10046H_CVBER_CTRL	 0x4F
+#define TDA10046H_AGC_IF_LEVEL	 0x52
+#define TDA10046H_CODE_CPT	 0x57
+#define TDA10046H_CODE_IN	 0x58
+
+
+static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data)
+{
+	int ret;
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr=0, .flags=0, .buf=buf, .len=2 };
+
+	dprintk("%s: reg=0x%x, data=0x%x\n", __FUNCTION__, reg, data);
+
+	msg.addr = state->config->demod_address;
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
+			__FUNCTION__, reg, data, ret);
+
+	dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__,
+		reg, data, ret);
+	return (ret != 1) ? -1 : 0;
+}
+
+static int tda1004x_read_byte(struct tda1004x_state *state, int reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {{ .addr=0, .flags=0, .buf=b0, .len=1},
+				{ .addr=0, .flags=I2C_M_RD, .buf=b1, .len = 1}};
+
+	dprintk("%s: reg=0x%x\n", __FUNCTION__, reg);
+
+	msg[0].addr = state->config->demod_address;
+	msg[1].addr = state->config->demod_address;
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg,
+			ret);
+		return -1;
+	}
+
+	dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__,
+		reg, b1[0], ret);
+	return b1[0];
+}
+
+static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data)
+{
+	int val;
+	dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __FUNCTION__, reg,
+		mask, data);
+
+	// read a byte and check
+	val = tda1004x_read_byte(state, reg);
+	if (val < 0)
+		return val;
+
+	// mask if off
+	val = val & ~mask;
+	val |= data & 0xff;
+
+	// write it out again
+	return tda1004x_write_byteI(state, reg, val);
+}
+
+static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len)
+{
+	int i;
+	int result;
+
+	dprintk("%s: reg=0x%x, len=0x%x\n", __FUNCTION__, reg, len);
+
+	result = 0;
+	for (i = 0; i < len; i++) {
+		result = tda1004x_write_byteI(state, reg + i, buf[i]);
+		if (result != 0)
+			break;
+	}
+
+	return result;
+}
+
+static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state)
+{
+	int result;
+	dprintk("%s\n", __FUNCTION__);
+
+	result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2);
+	msleep(1);
+	return result;
+}
+
+static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state)
+{
+	dprintk("%s\n", __FUNCTION__);
+
+	return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0);
+}
+
+static int tda10045h_set_bandwidth(struct tda1004x_state *state,
+				   fe_bandwidth_t bandwidth)
+{
+	static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f };
+	static u8 bandwidth_7mhz[] = { 0x02, 0x00, 0x37, 0x00, 0x4a, 0x2f, 0x6d, 0x76, 0xdb };
+	static u8 bandwidth_8mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x48, 0x17, 0x89, 0xc7, 0x14 };
+
+	switch (bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz));
+		break;
+
+	case BANDWIDTH_7_MHZ:
+		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz));
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz));
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0);
+
+	return 0;
+}
+
+static int tda10046h_set_bandwidth(struct tda1004x_state *state,
+				   fe_bandwidth_t bandwidth)
+{
+	static u8 bandwidth_6mhz[] = { 0x80, 0x15, 0xfe, 0xab, 0x8e };
+	static u8 bandwidth_7mhz[] = { 0x6e, 0x02, 0x53, 0xc8, 0x25 };
+	static u8 bandwidth_8mhz[] = { 0x60, 0x12, 0xa8, 0xe4, 0xbd };
+
+	switch (bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz));
+		break;
+
+	case BANDWIDTH_7_MHZ:
+		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz));
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz));
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tda1004x_do_upload(struct tda1004x_state *state,
+			      unsigned char *mem, unsigned int len,
+			      u8 dspCodeCounterReg, u8 dspCodeInReg)
+{
+	u8 buf[65];
+	struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = buf,.len = 0 };
+	int tx_size;
+	int pos = 0;
+
+	/* clear code counter */
+	tda1004x_write_byteI(state, dspCodeCounterReg, 0);
+	fw_msg.addr = state->config->demod_address;
+
+	buf[0] = dspCodeInReg;
+	while (pos != len) {
+
+		// work out how much to send this time
+		tx_size = len - pos;
+		if (tx_size > 0x10) {
+			tx_size = 0x10;
+		}
+
+		// send the chunk
+		memcpy(buf + 1, mem + pos, tx_size);
+		fw_msg.len = tx_size + 1;
+		if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
+			printk("tda1004x: Error during firmware upload\n");
+			return -EIO;
+		}
+		pos += tx_size;
+
+		dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, pos);
+	}
+	// give the DSP a chance to settle 03/10/05 Hac
+	msleep(100);
+	
+	return 0;
+}
+
+static int tda1004x_check_upload_ok(struct tda1004x_state *state, u8 dspVersion)
+{
+	u8 data1, data2;
+	unsigned long timeout;
+
+	if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
+		timeout = jiffies + 2 * HZ;
+		while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) {
+			if (time_after(jiffies, timeout)) {
+				printk("tda1004x: timeout waiting for DSP ready\n");
+				break;
+			}
+			msleep(1);
+		}
+	}
+	else
+		msleep(100);
+
+	// check upload was OK 
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
+	tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67);
+
+	data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1);
+	data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2);
+	printk("tda1004x: found firmware revision %x %x\n", data1, data2);
+	if (data1 != 0x67 || data2 < 0x20 || data2 > 0x2a) {
+		return -EIO;
+	}
+	return 0;
+}
+
+
+static int tda10045_fwupload(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int ret;
+	const struct firmware *fw;
+
+
+	/* don't re-upload unless necessary */
+	if (tda1004x_check_upload_ok(state, 0x2c) == 0) return 0;
+
+	/* request the firmware, this will block until someone uploads it */
+	printk("tda1004x: waiting for firmware upload...\n");
+	ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
+	if (ret) {
+		printk("tda1004x: no firmware upload (timeout or file not found?)\n");
+	   	return ret;
+	}
+
+	/* reset chip */
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
+
+	/* set parameters */
+	tda10045h_set_bandwidth(state, BANDWIDTH_8_MHZ);
+	msleep(10);
+
+	ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN);
+	if (ret)
+		return ret;
+
+	/* wait for DSP to initialise */
+	/* DSPREADY doesn't seem to work on the TDA10045H */
+	msleep(100);
+
+	return tda1004x_check_upload_ok(state, 0x2c);
+}
+
+
+static void tda10046_init_plls(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL1, 0xf0); 
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10); // PLL M = 10
+	if (state->config->xtal_freq == TDA10046_XTAL_4M ) {
+		printk("%s: setting up PLLs for a 4 MHz Xtal\n", __FUNCTION__);
+		tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0
+	}
+	else {
+		printk("%s: setting up PLLs for a 16 MHz Xtal\n", __FUNCTION__);	
+		tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 3); // PLL P = 0, N = 3
+    }
+	tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99);
+    switch (state->config->if_freq) {
+	case TDA10046_FREQ_DEFAULT:
+		tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
+		tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c);
+		break;
+	case TDA10046_FREQ_3613:
+		tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
+		tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x13);
+		break;
+	}
+	tda10046h_set_bandwidth(state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz
+}
+
+
+
+static int tda10046_fwupload(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int ret;
+	const struct firmware *fw;
+
+	/* reset + wake up chip */
+	tda1004x_write_byteI(state, TDA1004X_CONFC4, 0);
+	tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
+    /* let the clocks recover from sleep */
+	msleep(5);
+
+	/* don't re-upload unless necessary */
+	if (tda1004x_check_upload_ok(state, 0x20) == 0) return 0;
+
+	/* set parameters */
+	tda10046_init_plls(fe);
+
+	if (state->config->request_firmware != NULL) {
+		/* request the firmware, this will block until someone uploads it */
+		printk("tda1004x: waiting for firmware upload...\n");
+		ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE);
+		if (ret) {
+			printk("tda1004x: no firmware upload (timeout or file not found?)\n");
+   	   		return ret;
+		}
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
+		ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
+		if (ret)
+			return ret;
+	}
+	else {
+		/* boot from firmware eeprom */
+		/* Hac Note: we might need to do some GPIO Magic here */
+		printk("tda1004x: booting from eeprom\n");
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4);
+    	mdelay(300);
+	}
+	return tda1004x_check_upload_ok(state, 0x20);
+}
+
+
+
+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;
+}
+
+int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	return tda1004x_write_byteI(state, reg, data);
+}
+
+
+
+static int tda10045_init(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (state->initialised) return 0;
+
+	if (tda10045_fwupload(fe)) {
+		printk("tda1004x: firmware upload failed\n");
+		return -EIO;
+		}
+
+	tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC
+
+	// Init the PLL
+	if (state->config->pll_init) {
+		tda1004x_enable_tuner_i2c(state);
+		state->config->pll_init(fe);
+		tda1004x_disable_tuner_i2c(state);
+	}
+
+	// tda setup
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+	tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer
+	tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset
+	tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface
+	tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity
+	tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e);
+
+	tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk);
+
+	state->initialised = 1;
+	return 0;
+}
+
+
+
+static int tda10046_init(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	if (state->initialised) 
+		return 0;
+
+	if (tda10046_fwupload(fe)) {
+		printk("tda1004x: firmware upload failed\n");
+			return -EIO;
+		}
+
+	// Init the tuner PLL
+	if (state->config->pll_init) {
+		tda1004x_enable_tuner_i2c(state);
+		state->config->pll_init(fe); 
+		tda1004x_disable_tuner_i2c(state);
+	}
+
+	// tda setup
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+	tda1004x_write_byteI(state, TDA1004X_AUTO, 7); // select HP stream
+	tda1004x_write_byteI(state, TDA1004X_CONFC1, 8); // disable pulse killer
+	
+	tda10046_init_plls(fe);
+    switch (state->config->agc_config) {
+	case TDA10046_AGC_DEFAULT:
+		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup 
+		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+		break;
+	case TDA10046_AGC_IFO_AUTO_NEG:
+		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup Hac : was 0
+		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+		break;
+	}
+	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on
+	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0);	  // }
+	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
+	tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0);	  // }
+	tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff);  // }
+	tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1
+	tda1004x_write_byteI(state, TDA10046H_CVBER_CTRL, 0x1a); // 10^6 VBER measurement bits
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config
+	tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); 
+
+	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup
+	tda1004x_write_byteI(state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config
+	tda1004x_write_byteI(state, TDA10046H_GPIO_SELECT, 8); // GPIO select
+
+	state->initialised = 1;
+	return 0;
+}
+
+static int tda1004x_set_fe(struct dvb_frontend* fe,
+			   struct dvb_frontend_parameters *fe_params)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+	int inversion;
+
+	if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
+		// setup auto offset
+		tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0);
+
+		// disable agc_conf[2]
+		tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0);
+	}
+
+	// set frequency
+	tda1004x_enable_tuner_i2c(state);
+	state->config->pll_set(fe, fe_params);
+	tda1004x_disable_tuner_i2c(state);
+
+	// Hardcoded to use auto as much as possible on the TDA10045 as it
+	// is very unreliable if AUTO mode is _not_ used.
+	if (state->demod_type == TDA1004X_DEMOD_TDA10045) {
+	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(state, TDA1004X_AUTO, 1, 1);	// enable auto
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0);	// turn off constellation bits
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0);	// turn off hierarchy bits
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0);	// turn off FEC bits
+	} else {
+		tda1004x_write_mask(state, 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(state, TDA1004X_IN_CONF2, 7, tmp);
+
+		// set LP FEC
+		tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_LP);
+		if (tmp < 0) return tmp;
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3);
+
+		// set constellation
+		switch (fe_params->u.ofdm.constellation) {
+		case QPSK:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0);
+			break;
+
+		case QAM_16:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1);
+			break;
+
+		case QAM_64:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+
+		// set hierarchy
+		switch (fe_params->u.ofdm.hierarchy_information) {
+		case HIERARCHY_NONE:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5);
+			break;
+
+		case HIERARCHY_1:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5);
+			break;
+
+		case HIERARCHY_2:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5);
+			break;
+
+		case HIERARCHY_4:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	// set bandwidth
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		tda10045h_set_bandwidth(state, fe_params->u.ofdm.bandwidth);
+		break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		tda10046h_set_bandwidth(state, fe_params->u.ofdm.bandwidth);
+		break;
+	}
+
+	// set inversion
+	inversion = fe_params->inversion;
+	if (state->config->invert) inversion = inversion ? INVERSION_OFF : INVERSION_ON;
+	switch (inversion) {
+	case INVERSION_OFF:
+		tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0);
+		break;
+
+	case INVERSION_ON:
+		tda1004x_write_mask(state, 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(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
+		break;
+
+	case GUARD_INTERVAL_1_16:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2);
+		break;
+
+	case GUARD_INTERVAL_1_8:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2);
+		break;
+
+	case GUARD_INTERVAL_1_4:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2);
+		break;
+
+	case GUARD_INTERVAL_AUTO:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2);
+		tda1004x_write_mask(state, 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(state, TDA1004X_AUTO, 4, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4);
+		break;
+
+	case TRANSMISSION_MODE_8K:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4);
+		break;
+
+	case TRANSMISSION_MODE_AUTO:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// start the lock
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
+		msleep(10);
+		break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40);
+		msleep(10);
+		break;
+	}
+
+	return 0;
+}
+
+static int tda1004x_get_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	// inversion status
+	fe_params->inversion = INVERSION_OFF;
+	if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) {
+		fe_params->inversion = INVERSION_ON;
+	}
+	if (state->config->invert) fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON;
+
+	// bandwidth
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) {
+		case 0x14:
+			fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+			break;
+		case 0xdb:
+			fe_params->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+			break;
+		case 0x4f:
+			fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+			break;
+		}
+		break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		switch (tda1004x_read_byte(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 =
+	    tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7);
+	fe_params->u.ofdm.code_rate_LP =
+	    tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7);
+
+	// constellation
+	switch (tda1004x_read_byte(state, 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(state, TDA1004X_OUT_CONF1) & 0x10) {
+		fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+	}
+
+	// guard interval
+	switch ((tda1004x_read_byte(state, 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(state, 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;
+	}
+
+	return 0;
+}
+
+static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int status;
+	int cber;
+	int vber;
+
+	// read status
+	status = tda1004x_read_byte(state, 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(state, TDA1004X_CBER_LSB);
+		if (cber == -1) return -EIO;
+		status = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
+		if (status == -1) return -EIO;
+		cber |= (status << 8);
+		tda1004x_read_byte(state, 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(state, TDA1004X_VBER_LSB);
+		if (vber == -1) return -EIO;
+		status = tda1004x_read_byte(state, TDA1004X_VBER_MID);
+		if (status == -1) return -EIO;
+		vber |= (status << 8);
+		status = tda1004x_read_byte(state, TDA1004X_VBER_MSB);
+		if (status == -1) return -EIO;
+		vber |= ((status << 16) & 0x0f);
+		tda1004x_read_byte(state, 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
+	dprintk("%s: fe_status=0x%x\n", __FUNCTION__, *fe_status);
+	return 0;
+}
+
+static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+	int reg = 0;
+
+	// determine the register to use
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		reg = TDA10045H_S_AGC;
+		break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		reg = TDA10046H_AGC_IF_LEVEL;
+		break;
+	}
+
+	// read it
+	tmp = tda1004x_read_byte(state, reg);
+	if (tmp < 0)
+		return -EIO;
+
+	*signal = (tmp << 8) | tmp;
+	dprintk("%s: signal=0x%x\n", __FUNCTION__, *signal);
+	return 0;
+}
+
+static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+
+	// read it
+	tmp = tda1004x_read_byte(state, TDA1004X_SNR);
+	if (tmp < 0)
+		return -EIO;
+	if (tmp) {
+		tmp = 255 - tmp;
+	}
+
+	*snr = ((tmp << 8) | tmp);
+	dprintk("%s: snr=0x%x\n", __FUNCTION__, *snr);
+	return 0;
+}
+
+static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+	int tmp2;
+	int counter;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	// read the UCBLOCKS and reset
+	counter = 0;
+	tmp = tda1004x_read_byte(state, TDA1004X_UNCOR);
+	if (tmp < 0)
+		return -EIO;
+	tmp &= 0x7f;
+	while (counter++ < 5) {
+		tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+
+		tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR);
+		if (tmp2 < 0)
+			return -EIO;
+		tmp2 &= 0x7f;
+		if ((tmp2 < tmp) || (tmp2 == 0))
+			break;
+	}
+
+	if (tmp != 0x7f) {
+		*ucblocks = tmp;
+	} else {
+		*ucblocks = 0xffffffff;
+	}
+	dprintk("%s: ucblocks=0x%x\n", __FUNCTION__, *ucblocks);
+	return 0;
+}
+
+static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+
+	// read it in
+	tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
+	if (tmp < 0) return -EIO;
+	*ber = tmp << 1;
+	tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
+	if (tmp < 0) return -EIO;
+	*ber |= (tmp << 9);
+	tda1004x_read_byte(state, TDA1004X_CBER_RESET);
+
+	dprintk("%s: ber=0x%x\n", __FUNCTION__, *ber);
+	return 0;
+}
+
+static int tda1004x_sleep(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10);
+			break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
+			break;
+		}
+	state->initialised = 0;
+
+			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 = (struct tda1004x_state*) fe->demodulator_priv;
+	kfree(state);
+	}
+
+static struct dvb_frontend_ops tda10045_ops;
+
+struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+				     struct i2c_adapter* i2c)
+{
+	struct tda1004x_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda10045_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+	state->demod_type = TDA1004X_DEMOD_TDA10045;
+
+	/* check if the demod is there */
+	if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x25) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+	}
+
+static struct dvb_frontend_ops tda10046_ops;
+
+struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+				     struct i2c_adapter* i2c)
+{
+	struct tda1004x_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	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;
+
+	/* check if the demod is there */
+	if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+		}
+
+static struct dvb_frontend_ops tda10045_ops = {
+
+	.info = {
+		.name = "Philips TDA10045H DVB-T",
+		.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
+	},
+
+	.release = tda1004x_release,
+
+	.init = tda10045_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,
+};
+
+static struct dvb_frontend_ops tda10046_ops = {
+
+	.info = {
+		.name = "Philips TDA10046H DVB-T",
+		.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
+	},
+
+	.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,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator");
+MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10045_attach);
+EXPORT_SYMBOL(tda10046_attach);
+EXPORT_SYMBOL(tda1004x_write_byte);
diff -uprNw video4linux.orig/tda1004x.h video4linux/tda1004x.h
--- video4linux.orig/tda1004x.h	1970-01-01 01:00:00.000000000 +0100
+++ video4linux/tda1004x.h	2005-03-31 00:26:06.000000000 +0200
@@ -0,0 +1,75 @@
+  /*
+     Driver for Philips tda1004xh OFDM Frontend
+
+     (c) 2004 Andrew de Quincey
+
+     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.
+
+   */
+
+#ifndef TDA1004X_H
+#define TDA1004X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+#define TDA10046_XTAL_4M  0
+#define TDA10046_XTAL_16M 1
+
+#define TDA10046_AGC_DEFAULT 0         /* original configuration */
+#define TDA10046_AGC_IFO_AUTO_NEG 1    /* IF AGC only, automatic, negtive */
+
+#define TDA10046_FREQ_DEFAULT 0        /* original config, 36,166 MHZ */
+#define TDA10046_FREQ_3613 1           /* 36,13 MHZ */
+
+struct tda1004x_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* does the "inversion" need inverted? */
+	u8 invert:1;
+
+	/* Does the OCLK signal need inverted? */
+	u8 invert_oclk:1;
+	
+	/* Xtal frequency, 4 or 16MHz*/
+	u8 xtal_freq;          
+	
+	/* IF frequency */
+	u8 if_freq;
+	
+	/* AGC configuration */
+	u8 agc_config;
+	
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+	/* request firmware for device */
+	/* set this to NULL if the card has a firmware EEPROM */
+	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+					    struct i2c_adapter* i2c);
+
+extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+					    struct i2c_adapter* i2c);
+
+extern int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data);
+
+#endif // TDA1004X_H
-------------- next part --------------
diff -uprNw video4linux.orig/ChangeLog video4linux/ChangeLog
--- video4linux.orig/ChangeLog	2005-04-26 11:07:03.000000000 +0200
+++ video4linux/ChangeLog	2005-04-30 02:08:57.000000000 +0200
@@ -1,14 +1,79 @@
-2005-04-26 11:01  kraxel
+2005-04-29 Hartmut Hackmann (hartmut.hackmann@xxxxxxxxxxx)
 
-	* README.patches:
+	* Makefile
 
-	- update README.patches
+	- added driver for TDA10046
 
-2005-04-04 00:14  kraxel
+	* tda1004x.c tda1004x.h
 
-	* README.patches:
+	- added config options and code for
+	  + 16 MHZ crystal
+	  + different IF frequencies
+	  + different AGC controls
+	  + boot the DSP from on board EEPROM
 
-	- Final commit: add README.patches
+	- be more relaxed with different firmware revisions
+	
+	- code cleanup, rely less on default settings
+
+2005-04-29 Hartmut Hackmann (hartmut.hackmann@xxxxxxxxxxx)
+
+	* saa7134-cards.c saa7134.c saa7134-dvb-c
+	
+	- introduced / modified support for Medion MD7134 / Philips TOUGH reference design
+
+
+2005-04-29 Hartmut Hackmann (hartmut.hackmann@xxxxxxxxxxx)
+
+	* saa7134-core.c
+	
+	- distinguish DVB-T and analog only version of the MD7134 board
+	
+2005-04-29 Hartmut Hackmann (hartmut.hackmann@xxxxxxxxxxx)
+
+	* saa7134-oss.c
+
+    - fixed DMA memory allocation and setup
+	
+	- fixed odd / even buffer assignment
+	
+2005-04-29 Hartmut Hackmann (actually Gerd Knorr)
+
+	* saa7134-ts.c
+	
+	- fixed odd / even buffer assignment
+
+2005-04-29 Hartmut Hackmann (hartmut.hackmann@xxxxxxxxxxx)
+
+	* saa7134-tvaudio.c
+
+    - fixed audio clock switching for oss sound streaming
+	
+2005-04-29 Hartmut Hackmann (hartmut.hackmann@xxxxxxxxxxx)
+
+	* saa7134-video.c
+
+    - reinitialize (MD7134) hybrid tuner for analog TV
+	  (if prior operation was DVB-T)
+	  
+	- at exit, ensure that planar mode is off, otherwise
+	  DVB-T will not work
+	  
+2005-04-29 Hartmut Hackmann (hartmut.hackmann@xxxxxxxxxxx)
+
+	* tda9887.c audiochip.h
+
+    - added i2c client call for FM radio with FMD1216 tuner	  
+	  
+2005-04-29 Hartmut Hackmann (hartmut.hackmann@xxxxxxxxxxx)
+
+	* tuner-core.c tuner-simple.c tuner.h
+
+    - added support for Philips FMD1216ME tuner
+	
+	- allow run time change of tuner type (for MD7134)
+	
+	- added i2c client call to reinitialize tuner
 
 2005-03-10 11:07  kraxel
 
diff -uprNw video4linux.orig/Makefile video4linux/Makefile
--- video4linux.orig/Makefile	2005-02-21 15:07:00.000000000 +0100
+++ video4linux/Makefile	2005-03-21 00:30:22.000000000 +0100
@@ -40,7 +40,7 @@ obj-$(CONFIG_VIDEO_TUNER)	+= tuner.o tda
 obj-$(CONFIG_VIDEO_TVAUDIO)	+= msp3400.o tvaudio.o tvmixer.o
 
 obj-$(CONFIG_VIDEO_CX88_DVB)	+= video-buf-dvb.o cx88-dvb.o cx22702.o dvb-pll.o or51132.o
-obj-$(CONFIG_VIDEO_SAA7134_DVB) += video-buf-dvb.o saa7134-dvb.o mt352.o
+obj-$(CONFIG_VIDEO_SAA7134_DVB) += video-buf-dvb.o saa7134-dvb.o mt352.o  tda1004x.o
 
 # 2.6-only stuff
 ifeq ($(VERSION).$(PATCHLEVEL),2.6)
diff -uprNw video4linux.orig/audiochip.h video4linux/audiochip.h
--- video4linux.orig/audiochip.h	2004-10-13 13:07:00.000000000 +0200
+++ video4linux/audiochip.h	2005-04-27 00:58:48.000000000 +0200
@@ -31,5 +31,5 @@
 
 /* misc stuff to pass around config info to i2c chips */
 #define AUDC_CONFIG_PINNACLE  _IOW('m',32,int)
-
+#define AUDC_CONFIG_PHILIPS_FMD _IO('m',33)
 #endif /* AUDIOCHIP_H */
diff -uprNw video4linux.orig/load_modules video4linux/load_modules
--- video4linux.orig/load_modules	1970-01-01 01:00:00.000000000 +0100
+++ video4linux/load_modules	2005-04-27 01:12:12.000000000 +0200
@@ -0,0 +1,16 @@
+modprobe i2c-core
+modprobe firmware_class
+insmod ./video-buf.ko
+modprobe dvb_core 
+modprobe videodev
+insmod ./ir-common.ko 
+insmod ./v4l1-compat.ko
+insmod ./v4l2-common.ko
+insmod ./tuner.ko tuner
+insmod ./tda9887.ko
+insmod ./tda1004x.ko   
+insmod ./mt352.ko
+insmod ./video-buf-dvb.ko 
+insmod ./saa7134.ko ts_nr_packets=128 oss=1 
+insmod ./saa7134-dvb.ko
+ 
diff -uprNw video4linux.orig/rm_modules video4linux/rm_modules
--- video4linux.orig/rm_modules	1970-01-01 01:00:00.000000000 +0100
+++ video4linux/rm_modules	2005-03-23 23:33:35.000000000 +0100
@@ -0,0 +1,4 @@
+rmmod saa7134_dvb tda1004x tda9887 mt352
+rmmod tuner saa7134
+rmmod v4l2_common v4l1_compat ir_common videodev video_buf_dvb 
+rmmod dvb_core video_buf 
diff -uprNw video4linux.orig/saa7134-cards.c video4linux/saa7134-cards.c
--- video4linux.orig/saa7134-cards.c	2005-03-07 13:07:00.000000000 +0100
+++ video4linux/saa7134-cards.c	2005-04-30 01:19:21.000000000 +0200
@@ -474,9 +474,8 @@ struct saa7134_board saa7134_boards[] = 
         },
 	[SAA7134_BOARD_MD7134] = {
 		.name           = "Medion 7134",
-		//.audio_clock    = 0x00200000,
 		.audio_clock    = 0x00187de7,
-		.tuner_type     = TUNER_PHILIPS_FM1216ME_MK3,
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
 		.tda9887_conf   = TDA9887_PRESENT,
 		.mpeg           = SAA7134_MPEG_DVB,
 		.inputs = {{
@@ -1546,6 +1545,30 @@ struct saa7134_board saa7134_boards[] = 
 //			.gpio = 0x4000,
 		}},
 	},
+	[SAA7134_BOARD_PHILIPS_TOUGH] = {
+		.name           = "Philips TOUGH DVB-T reference design",
+		.tuner_type	= TUNER_ABSENT,
+		.audio_clock    = 0x00187de7,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE1,
+		}},
+		.radio = {
+			.name   = name_radio,
+			.amux   = LINE2,
+		},
+	},
 };
 const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
 
@@ -1840,6 +1863,12 @@ struct pci_device_id saa7134_pci_tbl[] =
 		.driver_data  = SAA7134_BOARD_FLYDVBTDUO,
 
  	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+                .subvendor    = PCI_VENDOR_ID_PHILIPS,
+                .subdevice    = 0x2004,
+		.driver_data  = SAA7134_BOARD_PHILIPS_TOUGH,
+	},{
 		/* --- boards without eeprom + subsystem ID --- */
                 .vendor       = PCI_VENDOR_ID_PHILIPS,
                 .device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
diff -uprNw video4linux.orig/saa7134-core.c video4linux/saa7134-core.c
--- video4linux.orig/saa7134-core.c	2005-02-22 11:07:00.000000000 +0100
+++ video4linux/saa7134-core.c	2005-04-25 22:10:09.000000000 +0200
@@ -722,7 +722,6 @@ static int saa7134_hwinit1(struct saa713
 		   SAA7134_MAIN_CTRL_EVFE1 |
 		   SAA7134_MAIN_CTRL_EVFE2 |
 		   SAA7134_MAIN_CTRL_ESFE  |
-		   SAA7134_MAIN_CTRL_EBADC |
 		   SAA7134_MAIN_CTRL_EBDAC);
 
 	/* enable peripheral devices */
@@ -990,6 +989,22 @@ static int __devinit saa7134_initdev(str
 	msleep(100);
 	saa7134_i2c_register(dev);
 
+    /* Hac: if a card has variants, let's patch the data structures now */
+    if (dev->board == SAA7134_BOARD_MD7134) {
+		u8 buf;
+		int ret;
+		struct i2c_msg msg = { .addr=0x08, .flags=I2C_M_RD, .buf=&buf, .len = 1};
+		ret = i2c_transfer(&dev->i2c_adap, &msg, 1);
+		if (ret == 1) {
+			printk(KERN_INFO "%s: Board MD7134 has DVB-T\n", dev->name);
+		} else {
+			printk(KERN_INFO "%s: Board MD7134 is analog only\n", dev->name);
+			dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3;		
+			saa7134_boards[SAA7134_BOARD_MD7134].tuner_type = TUNER_PHILIPS_FM1216ME_MK3;			
+			saa7134_i2c_call_clients(dev,TUNER_SET_TYPE,&dev->tuner_type);
+		}
+	}		
+
 	/* initialize hardware #2 */
 	saa7134_board_init2(dev);
 	saa7134_hwinit2(dev);
diff -uprNw video4linux.orig/saa7134-dvb.c video4linux/saa7134-dvb.c
--- video4linux.orig/saa7134-dvb.c	2005-02-18 14:07:01.000000000 +0100
+++ video4linux/saa7134-dvb.c	2005-04-23 23:38:10.000000000 +0200
@@ -3,6 +3,9 @@
  *
  * (c) 2004 Gerd Knorr <kraxel@xxxxxxxxxxx> [SuSE Labs]
  *
+ *  Extended 3 / 2005 by Hartmut Hackmann to support various
+ *  cards with the tda10046 DVB-T channel decoder
+ *
  *  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
@@ -141,44 +144,231 @@ static struct mt352_config pinnacle_300i
 
 /* ------------------------------------------------------------------ */
 
-static int medion_cardbus_init(struct dvb_frontend* fe)
+static int philips_tu1216_pll_init(struct dvb_frontend *fe)
 {
-	/* anything to do here ??? */
+	struct saa7134_dev *dev = fe->dvb->priv;
+	static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+	struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
+
+	// setup PLL configuration
+	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+	msleep(1);
+
 	return 0;
 }
 
-static int medion_cardbus_pll_set(struct dvb_frontend* fe,
-				  struct dvb_frontend_parameters* params)
+static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
-	struct v4l2_frequency f;
+	u8 tuner_buf[4];
+	struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len =
+			sizeof(tuner_buf) };
+	int tuner_frequency = 0;
+	u8 band, cp, filter;
+
+	// determine charge pump
+	tuner_frequency = params->frequency + 36166000;
+	if (tuner_frequency < 87000000)
+		return -EINVAL;
+	else if (tuner_frequency < 130000000)
+		cp = 3;
+	else if (tuner_frequency < 160000000)
+		cp = 5;
+	else if (tuner_frequency < 200000000)
+		cp = 6;
+	else if (tuner_frequency < 290000000)
+		cp = 3;
+	else if (tuner_frequency < 420000000)
+		cp = 5;
+	else if (tuner_frequency < 480000000)
+		cp = 6;
+	else if (tuner_frequency < 620000000)
+		cp = 3;
+	else if (tuner_frequency < 830000000)
+		cp = 5;
+	else if (tuner_frequency < 895000000)
+		cp = 7;
+	else
+		return -EINVAL;
+
+	// determine band
+	if (params->frequency < 49000000)
+		return -EINVAL;
+	else if (params->frequency < 161000000)
+		band = 1;
+	else if (params->frequency < 444000000)
+		band = 2;
+	else if (params->frequency < 861000000)
+		band = 4;
+	else
+		return -EINVAL;
+
+	// setup PLL filter
+	switch (params->u.ofdm.bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		filter = 0;
+		break;
 
-	/*
-	 * this instructs tuner.o to set the frequency, the call will
-	 * end up in tuner_command(), VIDIOC_S_FREQUENCY switch.
-	 * tda9887.o will see that as well.
-	 */
-	f.tuner     = 0;
-	f.type      = V4L2_TUNER_DIGITAL_TV;
-	f.frequency = params->frequency / 1000 * 16 / 1000;
-	saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,&f);
+	case BANDWIDTH_7_MHZ:
+		filter = 0;
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		filter = 1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// calculate divisor
+	// ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+	tuner_frequency = (((params->frequency / 1000) * 6) + 217496) / 1000;
+
+	// setup tuner buffer
+	tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
+	tuner_buf[1] = tuner_frequency & 0xff;
+	tuner_buf[2] = 0xca;
+	tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+
+	msleep(1);
 	return 0;
 }
 
-static int fe_request_firmware(struct dvb_frontend* fe,
+static int philips_tu1216_request_firmware(struct dvb_frontend *fe,
 			       const struct firmware **fw, char* name)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
 	return request_firmware(fw, name, &dev->pci->dev);
 }
 	
-struct tda1004x_config medion_cardbus = {
-	.demod_address = 0x08,  /* not sure this is correct */
-	.invert        = 0,
+static struct tda1004x_config philips_tu1216_config = {
+
+	.demod_address = 0x8,
+	.invert = 1,
+	.invert_oclk = 1,
+	.xtal_freq     = TDA10046_XTAL_4M,
+	.agc_config    = TDA10046_AGC_DEFAULT,
+	.if_freq       = TDA10046_FREQ_DEFAULT,
+	.pll_init = philips_tu1216_pll_init,
+	.pll_set = philips_tu1216_pll_set,
+	.request_firmware = philips_tu1216_request_firmware,
+};
+
+/* ------------------------------------------------------------------ */
+
+
+static int philips_fmd1216_pll_init(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+    // this message is to set up ATC and ALC
+	static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 };
+	struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) };
+
+	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+	msleep(1);
+
+	return 0;
+}
+
+static int philips_fmd1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	u8 tuner_buf[4];
+	struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = tuner_buf,.len =
+			sizeof(tuner_buf) };
+	int tuner_frequency = 0;
+	int divider = 0;
+	u8 band, mode, cp;
+
+	// determine charge pump
+	tuner_frequency = params->frequency + 36130000;
+	if (tuner_frequency < 87000000)
+		return -EINVAL;
+    // low band
+	else if (tuner_frequency < 180000000) {
+		band = 1;
+		mode = 7;
+		cp   = 0;
+	} else if (tuner_frequency < 195000000) {
+		band = 1;
+		mode = 6;
+		cp   = 1;
+	// mid band	
+	} else if (tuner_frequency < 366000000) {
+	    if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) {
+			band = 10;
+		} else {
+		    band = 2;
+		}
+		mode = 7;
+		cp   = 0;
+	} else if (tuner_frequency < 478000000) {
+	    if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) {
+			band = 10;
+		} else {
+		    band = 2;
+		}
+		mode = 6;
+		cp   = 1;
+	// high band
+	} else if (tuner_frequency < 662000000) {
+	    if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) {
+			band = 12;
+		} else {
+		    band = 4;
+		}
+		mode = 7;
+		cp   = 0;
+	} else if (tuner_frequency < 840000000) {
+	    if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) {
+			band = 12;
+		} else {
+		    band = 4;
+		}
+		mode = 6;
+		cp   = 1;
+	} else {
+	    if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) {
+			band = 12;
+		} else {
+		    band = 4;
+		}
+		mode = 7;
+		cp   = 1;
+	
+	}
+	// calculate divisor
+	// ((36166000 + Finput) / 166666) rounded!
+    divider = (tuner_frequency + 83333) / 166667;	
+
+	// setup tuner buffer
+	tuner_buf[0] = (divider >> 8) & 0x7f;
+	tuner_buf[1] = divider & 0xff;
+	tuner_buf[2] = 0x80 | (cp << 6) | (mode  << 3) | 4;
+	tuner_buf[3] = 0x40 | band;
+
+	if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+	
+static struct tda1004x_config medion_cardbus = {
+	.demod_address = 0x08,  
+	.invert        = 1,
         .invert_oclk   = 0,
-        .pll_init      = medion_cardbus_init,
-        .pll_set       = medion_cardbus_pll_set,
-        .request_firmware = fe_request_firmware,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_IFO_AUTO_NEG,
+	.if_freq       = TDA10046_FREQ_3613,
+	.pll_init      = philips_fmd1216_pll_init,
+	.pll_set       = philips_fmd1216_pll_set,
+	.request_firmware = NULL,
 };
 
 /* ------------------------------------------------------------------ */
@@ -205,9 +395,10 @@ static int dvb_init(struct saa7134_dev *
 	case SAA7134_BOARD_MD7134:
 		dev->dvb.frontend = tda10046_attach(&medion_cardbus,
 						    &dev->i2c_adap);
-		if (NULL == dev->dvb.frontend)
-			printk("%s: Hmm, looks like this is the old MD7134 "
-			       "version without DVB-T support\n",dev->name);
+		break;
+	case SAA7134_BOARD_PHILIPS_TOUGH:
+		dev->dvb.frontend = tda10046_attach(&philips_tu1216_config,
+						    &dev->i2c_adap);
 		break;
 	default:
 		printk("%s: Huh? unknown DVB card?\n",dev->name);
@@ -227,8 +418,6 @@ static int dvb_fini(struct saa7134_dev *
 {
 	static int on  = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE;
 
-	printk("%s: %s\n",dev->name,__FUNCTION__);
-
 	switch (dev->board) {
 	case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
 		/* otherwise we don't detect the tuner on next insmod */
diff -uprNw video4linux.orig/saa7134-ts.c video4linux/saa7134-ts.c
--- video4linux.orig/saa7134-ts.c	2005-02-03 11:27:01.000000000 +0100
+++ video4linux/saa7134-ts.c	2005-04-18 00:14:13.000000000 +0200
@@ -221,10 +221,10 @@ void saa7134_irq_ts_done(struct saa7134_
 	if (dev->ts_q.curr) {
 		field = dev->ts_q.curr->vb.field;
 		if (field == V4L2_FIELD_TOP) {
-			if ((status & 0x100000) != 0x000000)
+			if ((status & 0x100000) != 0x100000)
 				goto done;
 		} else {
-			if ((status & 0x100000) != 0x100000)
+			if ((status & 0x100000) != 0x000000)
 				goto done;
 		}
 		saa7134_buffer_finish(dev,&dev->ts_q,STATE_DONE);
diff -uprNw video4linux.orig/saa7134-video.c video4linux/saa7134-video.c
--- video4linux.orig/saa7134-video.c	2005-02-15 17:07:00.000000000 +0100
+++ video4linux/saa7134-video.c	2005-04-28 00:51:11.000000000 +0200
@@ -1218,7 +1218,6 @@ static int video_open(struct inode *inod
 	struct list_head *list;
 	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	int radio = 0;
-
 	list_for_each(list,&saa7134_devlist) {
 		h = list_entry(list, struct saa7134_dev, devlist);
 		if (h->video_dev && (h->video_dev->minor == minor))
@@ -1235,6 +1234,12 @@ static int video_open(struct inode *inod
 	if (NULL == dev)
 		return -ENODEV;
 
+	if (dev->board == SAA7134_BOARD_MD7134) {
+	/* initialize the tuner for analog mode, necessary for some hybrid tuners.
+ 	   the tda9886 will be invisible in digital mode Hac: 04.05*/
+		saa7134_i2c_call_clients(dev, TUNER_SET_ANALOG, NULL);				
+	}
+
 	dprintk("open minor=%d radio=%d type=%s\n",minor,radio,
 		v4l2_type_names[type]);
 
@@ -1270,6 +1275,8 @@ static int video_open(struct inode *inod
 	if (fh->radio) {
 		/* switch to radio mode */
 		saa7134_tvaudio_setinput(dev,&card(dev).radio);
+		if(dev->board == SAA7134_BOARD_MD7134)
+			saa7134_i2c_call_clients(dev,AUDC_CONFIG_PHILIPS_FMD, NULL);
 		saa7134_i2c_call_clients(dev,AUDC_SET_RADIO,NULL);
 	} else {
 		/* switch to video/vbi mode */
@@ -1377,6 +1384,12 @@ static int video_release(struct inode *i
 		res_free(dev,fh,RESOURCE_VBI);
 	}
 
+	/* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/
+	saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0);
+	saa_andorb(SAA7134_OFMT_VIDEO_B, 0x1f, 0);
+	saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
+	saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
+	
 	/* free stuff */
 	videobuf_mmap_free(&fh->cap);
 	videobuf_mmap_free(&fh->vbi);
diff -uprNw video4linux.orig/saa7134.h video4linux/saa7134.h
--- video4linux.orig/saa7134.h	2005-03-07 13:07:00.000000000 +0100
+++ video4linux/saa7134.h	2005-03-24 02:09:32.000000000 +0100
@@ -197,6 +197,7 @@ struct saa7134_format {
 #define SAA7135_BOARD_ASUSTeK_TVFM7135 53
 #define SAA7134_BOARD_FLYTVPLATINUM_FM 54
 #define SAA7134_BOARD_FLYDVBTDUO 55
+#define SAA7134_BOARD_PHILIPS_TOUGH 56
 
 #define SAA7134_MAXBOARDS 8
 #define SAA7134_INPUT_MAX 8
diff -uprNw video4linux.orig/tda9887.c video4linux/tda9887.c
--- video4linux.orig/tda9887.c	2005-02-04 13:07:00.000000000 +0100
+++ video4linux/tda9887.c	2005-04-28 00:11:34.000000000 +0200
@@ -61,6 +61,7 @@ struct tda9887 {
 	unsigned int       radio;
 	unsigned int       config;
 	unsigned int       pinnacle_id;
+	unsigned int       philips_fmd_id;
 	unsigned int       using_v4l2;
 };
 
@@ -231,6 +232,13 @@ static struct tvnorm radio = {
 		  cRadioIF_38_90 ),
 };
 
+static struct tvnorm radio_philips_fmd = {
+	.name = "radio",
+	.b    = ( cFmRadio | cQSS | cAutoMuteFmActive),
+	.c    = ( cAudioGain6 | cDeemphasis50 | cDeemphasisOFF | 0x1f),
+	.e    = ( cGating_36 | cTunerGainLow | cRadioIF_38_90 | cAudioIF_5_5 ),
+};
+
 /* ---------------------------------------------------------------------- */
 
 static void dump_read_message(unsigned char *buf)
@@ -363,6 +371,9 @@ static int tda9887_set_tvnorm(struct tda
 	int i;
 
 	if (t->radio) {
+		if (t-> philips_fmd_id) 
+			norm = &radio_philips_fmd;
+		else	
 		norm = &radio;
 	} else {
 		for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
@@ -559,6 +570,10 @@ static int tda9887_configure(struct tda9
 	if (UNSET != t->pinnacle_id) {
 		tda9887_set_pinnacle(t,buf);
 	}
+	if ((t->radio) && (t->philips_fmd_id)) {
+		buf[1] &= ~cOutputPort1Inactive;
+		buf[1] &= ~cOutputPort2Inactive;
+	}
 	tda9887_set_config(t,buf);
 	tda9887_set_insmod(t,buf);
 
@@ -609,6 +624,7 @@ static int tda9887_attach(struct i2c_ada
 	t->client      = client_template;
 	t->std         = 0;
 	t->pinnacle_id = UNSET;
+	t->philips_fmd_id = 0;
         i2c_set_clientdata(&t->client, t);
         i2c_attach_client(&t->client);
 
@@ -675,6 +691,9 @@ tda9887_command(struct i2c_client *clien
 		tda9887_configure(t);
 		break;
 	}
+	case AUDC_CONFIG_PHILIPS_FMD:
+		t->philips_fmd_id = 1;
+		break;
 	case TDA9887_SET_CONFIG:
 	{
 		int *i = arg;
diff -uprNw video4linux.orig/tuner-core.c video4linux/tuner-core.c
--- video4linux.orig/tuner-core.c	2005-02-15 17:07:00.000000000 +0100
+++ video4linux/tuner-core.c	2005-04-26 00:59:10.000000000 +0200
@@ -149,8 +149,8 @@ static void set_type(struct i2c_client *
 		t->type = type;
 		return;
 	}
-	if (t->initialized)
-		/* run only once */
+	if ((t->initialized) && (t->type == type))
+		/* run only once except type change  Hac 04/05*/
 		return;
 
 	t->initialized = 1;
@@ -289,7 +289,9 @@ tuner_command(struct i2c_client *client,
 			break;
 		}
                 break;
-
+	case TUNER_SET_ANALOG:
+		default_tuner_init2(client);
+		break;
 	/* --- v4l ioctls --- */
 	/* take care: bttv does userspace copying, we'll get a
 	   kernel pointer here... */
diff -uprNw video4linux.orig/tuner-simple.c video4linux/tuner-simple.c
--- video4linux.orig/tuner-simple.c	2005-03-08 10:07:01.000000000 +0100
+++ video4linux/tuner-simple.c	2005-04-26 01:21:12.000000000 +0200
@@ -212,6 +212,8 @@ static struct tunertype tuners[] = {
           16*160.00,16*442.00,0x01,0x02,0x04,0xce,623 },
         { "Philips FQ1236A MK4", Philips, NTSC,
           16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 },
+	{ "Philips FMD1216ME MK3 Hybrid Tuner", Philips, PAL,
+	  16*160.00,16*442.00,0x51,0x52,0x54,0x86,623 },          
 
 };
 unsigned const int tuner_count = ARRAY_SIZE(tuners);
@@ -427,6 +429,7 @@ static void default_set_radio_freq(struc
 	switch (t->type) {
 	case TUNER_PHILIPS_FM1216ME_MK3:
 	case TUNER_PHILIPS_FM1236_MK3:
+	case TUNER_PHILIPS_FMD1216ME_MK3:
 		buffer[3] = 0x19;
 		break;
 	case TUNER_PHILIPS_FM1256_IH3:
@@ -463,9 +466,29 @@ int default_tuner_init(struct i2c_client
 	t->radio_freq = default_set_radio_freq;
 	t->has_signal = tuner_signal;
 	t->is_stereo  = tuner_stereo;
+	default_tuner_init2(c);
 	return 0;
 }
 
+void default_tuner_init2(struct i2c_client *c)
+{
+	struct tuner *t = i2c_get_clientdata(c);
+	unsigned char buffer[4];
+
+	switch (t->type) {
+	case TUNER_PHILIPS_FMD1216ME_MK3:
+		buffer[0] = 0x0b; 
+		buffer[1] = 0xdc; 
+		buffer[2] = 0x9c;
+		buffer[3] = 0x60;
+        i2c_master_send(c,buffer,4);
+		mdelay(1);
+		buffer[2] = 0x86;
+		buffer[3] = 0x54;
+        i2c_master_send(c,buffer,4);
+		break;
+	}		
+}
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * ---------------------------------------------------------------------------
diff -uprNw video4linux.orig/tuner.h video4linux/tuner.h
--- video4linux.orig/tuner.h	2005-03-08 10:07:01.000000000 +0100
+++ video4linux/tuner.h	2005-04-26 01:04:53.000000000 +0200
@@ -97,6 +97,7 @@
 
 #define TUNER_PHILIPS_FQ1216AME_MK4 56 /* Hauppauge PVR-150 PAL */
 #define TUNER_PHILIPS_FQ1236A_MK4 57   /* Hauppauge PVR-500MCE NTSC */
+#define TUNER_PHILIPS_FMD1216ME_MK3 58
 
 #define NOTUNER 0
 #define PAL     1	/* PAL_BG */
@@ -121,6 +122,7 @@
 
 #define TUNER_SET_TYPE               _IOW('t',1,int)    /* set tuner type */
 #define TUNER_SET_TVFREQ             _IOW('t',2,int)    /* set tv freq */
+#define TUNER_SET_ANALOG             _IO('t',6)         /* reinitialize analog tuner */
 
 #define  TDA9887_SET_CONFIG          _IOW('t',5,int)
 /* tv card specific */
@@ -178,6 +180,7 @@ extern unsigned const int tuner_count;
 extern int microtune_init(struct i2c_client *c);
 extern int tda8290_init(struct i2c_client *c);
 extern int default_tuner_init(struct i2c_client *c);
+extern void default_tuner_init2(struct i2c_client *c);
 
 #define tuner_warn(fmt, arg...) \
 	dev_printk(KERN_WARNING , &t->i2c.dev , fmt , ## arg)
-------------- next part --------------
diff -uprNw video4linux.orig/saa7134-oss.c video4linux/saa7134-oss.c
--- video4linux.orig/saa7134-oss.c	2004-12-10 14:07:00.000000000 +0100
+++ video4linux/saa7134-oss.c	2005-04-22 00:08:47.000000000 +0200
@@ -49,7 +49,6 @@ MODULE_PARM_DESC(oss_rate,"sample rate (
 
 static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks)
 {
-	blksize &= ~0xff;
 	if (blksize < 0x100)
 		blksize = 0x100;
 	if (blksize > 0x10000)
@@ -57,8 +56,6 @@ static int dsp_buffer_conf(struct saa713
 
 	if (blocks < 2)
 		blocks = 2;
-        while ((blksize * blocks) & ~PAGE_MASK)
-		blocks++;
 	if ((blksize * blocks) > 1024*1024)
 		blocks = 1024*1024 / blksize;
 
@@ -79,7 +76,7 @@ static int dsp_buffer_init(struct saa713
 		BUG();
 	videobuf_dma_init(&dev->oss.dma);
 	err = videobuf_dma_init_kernel(&dev->oss.dma, PCI_DMA_FROMDEVICE,
-				       dev->oss.bufsize >> PAGE_SHIFT);
+				       (dev->oss.bufsize + PAGE_SIZE) >> PAGE_SHIFT);
 	if (0 != err)
 		return err;
 	return 0;
@@ -163,10 +160,11 @@ static int dsp_rec_start(struct saa7134_
 			fmt |= 0x04;
 		fmt |= (TV == dev->oss.input) ? 0xc0 : 0x80;
 
-		saa_writeb(SAA7134_NUM_SAMPLES0, (dev->oss.blksize & 0x0000ff));
-		saa_writeb(SAA7134_NUM_SAMPLES1, (dev->oss.blksize & 0x00ff00) >>  8);
-		saa_writeb(SAA7134_NUM_SAMPLES2, (dev->oss.blksize & 0xff0000) >> 16);
+		saa_writeb(SAA7134_NUM_SAMPLES0, ((dev->oss.blksize - 1) & 0x0000ff));
+		saa_writeb(SAA7134_NUM_SAMPLES1, ((dev->oss.blksize - 1) & 0x00ff00) >>  8);
+		saa_writeb(SAA7134_NUM_SAMPLES2, ((dev->oss.blksize - 1) & 0xff0000) >> 16);
 		saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt);
+		
 		break;
 	case PCI_DEVICE_ID_PHILIPS_SAA7133:
 	case PCI_DEVICE_ID_PHILIPS_SAA7135:
@@ -550,6 +548,7 @@ mixer_recsrc_7134(struct saa7134_dev *de
 		saa_andorb(SAA7134_ANALOG_IO_SELECT,  0x08, analog_io);
 		saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0x80);
 		saa_andorb(SAA7134_SIF_SAMPLE_FREQ,   0x03, rate);
+
 		break;
 	}
 	return 0;
@@ -817,7 +816,7 @@ void saa7134_irq_oss_done(struct saa7134
 			reg = SAA7134_RS_BA1(6);
 	} else {
 		/* even */
-		if (0 == (dev->oss.dma_blk & 0x00))
+		if (1 == (dev->oss.dma_blk & 0x01))
 			reg = SAA7134_RS_BA2(6);
 	}
 	if (0 == reg) {
@@ -836,9 +835,9 @@ void saa7134_irq_oss_done(struct saa7134
 	next_blk = (dev->oss.dma_blk + 2) % dev->oss.blocks;
 	saa_writel(reg,next_blk * dev->oss.blksize);
 	if (oss_debug > 2)
-		dprintk("irq: ok, %s, next_blk=%d, addr=%x\n",
+		dprintk("irq: ok, %s, next_blk=%d, addr=%x, base=%x\n",
 			(status & 0x10000000) ? "even" : "odd ", next_blk,
-			next_blk * dev->oss.blksize);
+			next_blk * dev->oss.blksize, reg << 2);
 
 	/* update status & wake waiting readers */
 	dev->oss.dma_blk = (dev->oss.dma_blk + 1) % dev->oss.blocks;
diff -uprNw video4linux.orig/saa7134-tvaudio.c video4linux/saa7134-tvaudio.c
--- video4linux.orig/saa7134-tvaudio.c	2005-01-07 14:27:01.000000000 +0100
+++ video4linux/saa7134-tvaudio.c	2005-04-21 22:45:03.000000000 +0200
@@ -181,7 +181,8 @@ static void tvaudio_init(struct saa7134_
 	saa_writeb(SAA7134_AUDIO_CLOCK0,      clock        & 0xff);
 	saa_writeb(SAA7134_AUDIO_CLOCK1,     (clock >>  8) & 0xff);
 	saa_writeb(SAA7134_AUDIO_CLOCK2,     (clock >> 16) & 0xff);
-	saa_writeb(SAA7134_AUDIO_PLL_CTRL,   0x01);
+    // frame locked audio was reported not to be reliable
+	saa_writeb(SAA7134_AUDIO_PLL_CTRL,   0x02);
 
 	saa_writeb(SAA7134_NICAM_ERROR_LOW,  0x14);
 	saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50);
@@ -250,6 +251,11 @@ static void mute_input_7134(struct saa71
 	saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, ausel);
 	saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, ics);
 	saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, ocs);
+	// for oss, we need to change the clock configuration
+    if (in->amux == TV)
+		saa_andorb(SAA7134_SIF_SAMPLE_FREQ,   0x03, 0x00);
+	else
+		saa_andorb(SAA7134_SIF_SAMPLE_FREQ,   0x03, 0x01);
 
 	/* switch gpio-connected external audio mux */
 	if (0 == card(dev).gpiomask)

[Index of Archives]     [Linux Media]     [Video 4 Linux]     [Asterisk]     [Samba]     [Xorg]     [Xfree86]     [Linux USB]

  Powered by Linux