[PATCH] Quantek QT1010 tuner module

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

 



hei!
changes:
QT1010:
- old qt1010-code totally rewritten and put in own kernel module
- same enhancements as my earlier QT1010 125kHz patch
- tuner initialization
- register 1f calculation
- register 20 calculation
- register 25 calculation

gl861: (MSI Megasky)
- hack for enable ZL10353 / QT1010 I2C gate, fixed later..
- use new QT1010 module instead of old code

au6610: (Sigmatek DVB-110)
- hack for enable ZL10353 / QT1010 I2C gate, fixed later..
- use new QT1010 module instead of old code

m920x: (MSI Megasky)
- use new QT1010 module instead of old code

Tested successfully with au6610 and gl861 devices against fi-Yllas frequencies. Now it locks perfectly with both devices.

There is a "hack" to enable probable i2c gate in zl10535 demodulator. QT1010 doesn't respond to any i2c messages before we write 0x1a to demodulator register 0x62. In my understanding this should be fixed to demodulator code. It could be also possible that new qt1010 module does not get detected in m9206 based Megasky due to different demodulator and different gate. I couldn't test this due to lack of m9206 based Megasky.

Aapo, could you especially check and test m9206 based Megasky?
Christopher Pascoe, could you check patch (au6610/gl861) and clarify Zarlink register 62 meaning?
Carl Lundqvist and Andrew Waldram, could you also test in your countries.

All test reports are highly welcome.

Regards,
Antti Palosaari

Signed-off-by: Antti Palosaari <crope@xxxxxx>
--
              |||
             (0-0)
---------oOO--(_)--OOo--------------------------------------------
tel. +358 40 535 7322 | MSN Messenger crope@xxxxxx | www.palosaari.fi
-Kahta asiaa en ymmärrä.. C-kielen syntaksi ja naisten logiikka.."
diff -r 11fa5f80d049 linux/drivers/media/dvb/dvb-usb/au6610.c
--- a/linux/drivers/media/dvb/dvb-usb/au6610.c	Sun Nov 05 14:05:38 2006 -0500
+++ b/linux/drivers/media/dvb/dvb-usb/au6610.c	Wed Dec 13 00:12:46 2006 +0200
@@ -1,6 +1,6 @@
 /* DVB USB compliant linux driver for Sigmatek DVB-110 DVB-T USB2.0 receiver
  *
- * Copyright (C) 2006 Antti Palosaari (crope@xxxxxx)
+ * Copyright (C) 2006 Antti Palosaari <crope@xxxxxx>
  *
  *	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
@@ -18,7 +18,6 @@ static int dvb_usb_au6610_debug;
 static int dvb_usb_au6610_debug;
 module_param_named(debug, dvb_usb_au6610_debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
-
 
 static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
 			  u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
@@ -127,7 +126,6 @@ static struct zl10353_config au6610_zl10
 	.parallel_ts = 1,
 };
 
-
 static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
 {
 	if ((adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
@@ -136,6 +134,29 @@ static int au6610_zl10353_frontend_attac
 	}
 
 	return -EIO;
+}
+
+static struct qt1010_config au6610_qt1010_config = {
+	.i2c_address = 0xc4
+};
+
+static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	/* TODO FIXME; probably I2C gate.
+	QT1010 tuner does not respond before we write 0x1a to ZL10353 demodulator
+	register 0x62. This ought to be done somewhere in demodulator initialization.
+	This solution is temporary hack. */
+	u8 buf[2] = { 0x62, 0x1a };
+	struct i2c_msg msg = {
+		.addr = au6610_zl10353_config.demod_address, .flags = 0, .buf = buf, .len = 2
+	};
+	if (i2c_transfer(&adap->dev->i2c_adap, &msg, 1) != 1) {
+		printk(KERN_WARNING "au6610 tuner attach failed\n");
+		return -EREMOTEIO;
+	}
+	return dvb_attach(qt1010_attach,
+	                  adap->fe, &adap->dev->i2c_adap,
+	                  &au6610_qt1010_config) == NULL ? -ENODEV : 0;
 }
 
 /* DVB USB Driver stuff */
@@ -180,7 +201,7 @@ static struct dvb_usb_device_properties 
 	.adapter = {
 		{
 			.frontend_attach  = au6610_zl10353_frontend_attach,
-			.tuner_attach     = qt1010_tuner_attach,
+			.tuner_attach     = au6610_qt1010_tuner_attach,
 
 			.stream = {
 				.type = USB_ISOC,
diff -r 11fa5f80d049 linux/drivers/media/dvb/dvb-usb/gl861.c
--- a/linux/drivers/media/dvb/dvb-usb/gl861.c	Sun Nov 05 14:05:38 2006 -0500
+++ b/linux/drivers/media/dvb/dvb-usb/gl861.c	Wed Dec 13 01:01:14 2006 +0200
@@ -109,11 +109,34 @@ static int gl861_frontend_attach(struct 
 static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
 {
 	if ((adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
-				   &adap->dev->i2c_adap)) != NULL) {
+	                           &adap->dev->i2c_adap)) != NULL) {
 		return 0;
 	}
 
 	return -EIO;
+}
+
+static struct qt1010_config gl861_qt1010_config = {
+	.i2c_address = 0xc4
+};
+
+static int gl861_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	/* TODO FIXME; probably I2C gate.
+	QT1010 tuner does not respond before we write 0x1a to ZL10353 demodulator
+	register 0x62. This ought to be done somewhere in demodulator initialization.
+	This solution is temporary hack. */
+	u8 buf[2] = { 0x62, 0x1a };
+	struct i2c_msg msg = {
+		.addr = gl861_zl10353_config.demod_address, .flags = 0, .buf = buf, .len = 2
+	};
+	if (i2c_transfer(&adap->dev->i2c_adap, &msg, 1) != 1) {
+		printk(KERN_WARNING "gl861 tuner attach failed\n");
+		return -EREMOTEIO;
+	}
+	return dvb_attach(qt1010_attach,
+	                  adap->fe, &adap->dev->i2c_adap,
+	                  &gl861_qt1010_config) == NULL ? -ENODEV : 0;
 }
 
 /* DVB USB Driver stuff */
@@ -161,7 +184,7 @@ static struct dvb_usb_device_properties 
 	.adapter = {{
 
 		.frontend_attach  = gl861_frontend_attach,
-		.tuner_attach     = qt1010_tuner_attach,
+		.tuner_attach     = gl861_tuner_attach,
 
 		.stream = {
 			.type = USB_BULK,
diff -r 11fa5f80d049 linux/drivers/media/dvb/dvb-usb/m920x.c
--- a/linux/drivers/media/dvb/dvb-usb/m920x.c	Sun Nov 05 14:05:38 2006 -0500
+++ b/linux/drivers/media/dvb/dvb-usb/m920x.c	Wed Dec 13 00:15:14 2006 +0200
@@ -390,6 +390,17 @@ static int m9206_firmware_download(struc
 	return ret;
 }
 
+static struct qt1010_config megasky_qt1010_config = {
+	.i2c_address = 0xc4
+};
+
+static int megasky_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	return dvb_attach(qt1010_attach,
+	                  adap->fe, &adap->dev->i2c_adap,
+	                  &megasky_qt1010_config) == NULL ? -ENODEV : 0;
+}
+
 /* DVB USB Driver stuff */
 static struct dvb_usb_device_properties megasky_properties;
 
@@ -449,7 +460,7 @@ static struct dvb_usb_device_properties 
 		.pid_filter_ctrl  = m9206_pid_filter_ctrl,
 
 		.frontend_attach  = megasky_frontend_attach,
-		.tuner_attach     = qt1010_tuner_attach,
+		.tuner_attach     = megasky_tuner_attach,
 
 		.stream = {
 			.type = USB_BULK,
diff -r 11fa5f80d049 linux/drivers/media/dvb/frontends/Kconfig
--- a/linux/drivers/media/dvb/frontends/Kconfig	Sun Nov 05 14:05:38 2006 -0500
+++ b/linux/drivers/media/dvb/frontends/Kconfig	Tue Dec 12 04:38:07 2006 +0200
@@ -282,6 +282,13 @@ config DVB_TDA826X
 	help
 	  A DVB-S silicon tuner module. Say Y when you want to support this tuner.
 
+config DVB_TUNER_QT1010
+	tristate "Quantek QT1010 silicon tuner"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A driver for the silicon tuner QT1010 from Quantek.
+
 config DVB_TUNER_MT2060
 	tristate "Microtune MT2060 silicon IF tuner"
 	help
diff -r 11fa5f80d049 linux/drivers/media/dvb/frontends/Makefile
--- a/linux/drivers/media/dvb/frontends/Makefile	Sun Nov 05 14:05:38 2006 -0500
+++ b/linux/drivers/media/dvb/frontends/Makefile	Tue Dec 12 04:38:07 2006 +0200
@@ -37,4 +37,5 @@ obj-$(CONFIG_DVB_TDA10086) += tda10086.o
 obj-$(CONFIG_DVB_TDA10086) += tda10086.o
 obj-$(CONFIG_DVB_TDA826X) += tda826x.o
 obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
+obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o
 obj-$(CONFIG_DVB_TUA6100) += tua6100.o
diff -r 11fa5f80d049 linux/drivers/media/dvb/frontends/qt1010.h
--- a/linux/drivers/media/dvb/frontends/qt1010.h	Sun Nov 05 14:05:38 2006 -0500
+++ b/linux/drivers/media/dvb/frontends/qt1010.h	Wed Dec 13 00:53:47 2006 +0200
@@ -1,5 +1,8 @@
 /*
- *  qt1010.h - DVB-T Tuner support
+ *  Driver for Quantek QT1010 silicon tuner
+ *
+ *  Copyright (C) 2006 Antti Palosaari <crope@xxxxxx>
+ *                     Aapo Tahkola <aet@xxxxxxxxxxxxxx>
  *
  *  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
@@ -16,211 +19,35 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#ifndef _QT1010_H_
-#define _QT1010_H_
+#ifndef QT1010_H
+#define QT1010_H
 
-#define QT1010_W 0
-#define QT1010_R 1
-/* Not actual hw limits. */
-#define QT1010_MIN_STEP 2000000
-#define QT1010_MIN_FREQ 48000000
+#include "dvb_frontend.h"
 
-static int qt1010_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+struct qt1010_config {
+	u8 i2c_address;
+};
+
+/**
+ * Attach a qt1010 tuner to the supplied frontend structure.
+ *
+ * @param fe   frontend to attach to
+ * @param i2c  i2c adapter to use
+ * @param cfg  tuner hw based configuration
+ * @return fe  pointer on success, NULL on failure
+ */
+#if defined(CONFIG_DVB_TUNER_QT1010) || (defined(CONFIG_DVB_TUNER_QT1010_MODULE) && defined(MODULE))
+extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
+                                          struct i2c_adapter *i2c,
+                                          struct qt1010_config *cfg);
+#else
+static inline struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
+                                                 struct i2c_adapter *i2c,
+                                                 struct qt1010_config *cfg)
 {
-	int i;
-	int div, mod;
-	struct {
-		u8 read, reg, value;
-	} rd[46] = {	{ QT1010_W, 0x01, 0x80 },
-			{ QT1010_W, 0x02, 0x3f },
-			{ QT1010_W, 0x05, 0xff }, /* c */
-			{ QT1010_W, 0x06, 0x44 },
-			{ QT1010_W, 0x07, 0xff }, /* c */
-			{ QT1010_W, 0x08, 0x08 },
-			{ QT1010_W, 0x09, 0xff }, /* c */
-			{ QT1010_W, 0x0a, 0xff }, /* c */
-			{ QT1010_W, 0x0b, 0xff }, /* c */
-			{ QT1010_W, 0x0c, 0xe1 },
-			{ QT1010_W, 0x1a, 0xff }, /* 10 c */
-			{ QT1010_W, 0x1b, 0x00 },
-			{ QT1010_W, 0x1c, 0x89 },
-			{ QT1010_W, 0x11, 0xff }, /* c */
-			{ QT1010_W, 0x12, 0x91 },
-			{ QT1010_W, 0x22, 0xff }, /* c */
-			{ QT1010_W, 0x1e, 0x00 },
-			{ QT1010_W, 0x1e, 0xd0 },
-			{ QT1010_R, 0x22, 0xff }, /* c read */
-			{ QT1010_W, 0x1e, 0x00 },
-			{ QT1010_R, 0x05, 0xff }, /* 20 c read */
-			{ QT1010_R, 0x22, 0xff }, /* c read */
-			{ QT1010_W, 0x23, 0xd0 },
-			{ QT1010_W, 0x1e, 0x00 },
-			{ QT1010_W, 0x1e, 0xe0 },
-			{ QT1010_R, 0x23, 0xff }, /* c read */
-			{ QT1010_W, 0x1e, 0x00 },
-			{ QT1010_W, 0x24, 0xd0 },
-			{ QT1010_W, 0x1e, 0x00 },
-			{ QT1010_W, 0x1e, 0xf0 },
-			{ QT1010_R, 0x24, 0xff }, /* 30 c read */
-			{ QT1010_W, 0x1e, 0x00 },
-			{ QT1010_W, 0x14, 0x7f },
-			{ QT1010_W, 0x15, 0x7f },
-			{ QT1010_W, 0x05, 0xff }, /* c */
-			{ QT1010_W, 0x06, 0x00 },
-			{ QT1010_W, 0x15, 0x1f },
-			{ QT1010_W, 0x16, 0xff },
-			{ QT1010_W, 0x18, 0xff },
-			{ QT1010_W, 0x1f, 0xff }, /* c */
-			{ QT1010_W, 0x20, 0xff }, /* 40 c */
-			{ QT1010_W, 0x21, 0x53 },
-			{ QT1010_W, 0x25, 0xbd },
-			{ QT1010_W, 0x26, 0x15 },
-			{ QT1010_W, 0x02, 0x00 },
-			{ QT1010_W, 0x01, 0x00 },
-			};
-	struct i2c_msg msg;
-	struct dvb_usb_adapter *adap = fe->dvb->priv;
-	unsigned long freq = params->frequency;
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+	return NULL;
+}
+#endif // CONFIG_DVB_TUNER_QT1010
 
-	if (freq % QT1010_MIN_STEP)
-		printk("frequency not supported.\n");
-
-	div = (freq - QT1010_MIN_FREQ) / QT1010_MIN_STEP;
-	mod = (div + 16 - 9) % 16;
-
-	/* 0x5 */
-	if (div >= 377)
-		rd[2].value = 0x74;
-	else if (div >=  265)
-		rd[2].value = 0x54;
-	else if (div >=  121)
-		rd[2].value = 0x34;
-	else
-		rd[2].value = 0x14;
-
-	/* 0x7 */
-	rd[4].value = (((freq - QT1010_MIN_FREQ) / 1000000) * 9975 + 12960000) / 320000;
-
-	/* 09 */
-	if (mod < 4)
-		rd[6].value = 0x1d;
-	else
-		rd[6].value = 0x1c;
-
-	/* 0a */
-	if (mod < 2)
-		rd[7].value = 0x09;
-	else if (mod < 4)
-		rd[7].value = 0x08;
-	else if (mod < 6)
-		rd[7].value = 0x0f;
-	else if (mod < 8)
-		rd[7].value = 0x0e;
-	else if (mod < 10)
-		rd[7].value = 0x0d;
-	else if (mod < 12)
-		rd[7].value = 0x0c;
-	else if (mod < 14)
-		rd[7].value = 0x0b;
-	else
-		rd[7].value = 0x0a;
-
-	/* 0b */
-	if (div & 1)
-		rd[8].value = 0x45;
-	else
-		rd[8].value = 0x44;
-
-	/* 1a */
-	if (div & 1)
-		rd[10].value = 0x78;
-	else
-		rd[10].value = 0xf8;
-
-	/* 11 */
-	if (div >= 265)
-		rd[13].value = 0xf9;
-	else if (div >=  121)
-		rd[13].value = 0xfd;
-	else
-		rd[13].value = 0xf9;
-
-	/* 22 */
-	if (div < 201)
-		rd[15].value = 0xd0;
-	else if (div < 217)
-		rd[15].value = 0xd3;
-	else if (div < 233)
-		rd[15].value = 0xd6;
-	else if (div < 249)
-		rd[15].value = 0xd9;
-	else if (div < 265)
-		rd[15].value = 0xda;
-	else
-		rd[15].value = 0xd0;
-
-	/* 05 */
-	if (div >= 377)
-		rd[34].value = 0x70;
-	else if (div >=  265)
-		rd[34].value = 0x50;
-	else if (div >=  121)
-		rd[34].value = 0x30;
-	else
-		rd[34].value = 0x10;
-
-	/* 1f */
-	if (mod < 4)
-		rd[39].value = 0x64;
-	else if (mod < 6)
-		rd[39].value = 0x66;
-	else if (mod < 8)
-		rd[39].value = 0x67;
-	else if (mod < 12)
-		rd[39].value = 0x68;
-	else if (mod < 14)
-		rd[39].value = 0x69;
-	else
-		rd[39].value = 0x6a;
-
-	/* 20 */
-	if (mod < 4)
-		rd[40].value = 0x10;
-	else if (mod < 6)
-		rd[40].value = 0x11;
-	else if (mod < 10)
-		rd[40].value = 0x12;
-	else if (mod < 12)
-		rd[40].value = 0x13;
-	else if (mod < 14)
-		rd[40].value = 0x14;
-	else
-		rd[40].value = 0x15;
-
-	for (i = 0; i < sizeof(rd) / sizeof(*rd); i++) {
-		if (rd[i].read)
-			continue;
-
-		msg.flags = 0;
-		msg.len = 2;
-		msg.addr = adap->dev->adapter[0].pll_addr;
-		msg.buf = &rd[i].reg;
-
-		if (i2c_transfer(&adap->dev->i2c_adap, &msg, 1) != 1) {
-			printk("tuner write failed\n");
-			return -EIO;
-		}
-	}
-
-	return 0;
-}
-
-static int qt1010_tuner_attach(struct dvb_usb_adapter *adap)
-{
-	adap->pll_addr = 0xc4;
-	adap->pll_desc = NULL;
-	adap->fe->ops.tuner_ops.set_params = qt1010_set_params;
-
-	return 0;
-}
 #endif
diff -r 11fa5f80d049 linux/drivers/media/dvb/frontends/qt1010.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/qt1010.c	Wed Dec 13 00:21:23 2006 +0200
@@ -0,0 +1,455 @@
+/*
+ *  Driver for Quantek QT1010 silicon tuner
+ *
+ *  Copyright (C) 2006 Antti Palosaari <crope@xxxxxx>
+ *                     Aapo Tahkola <aet@xxxxxxxxxxxxxx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "qt1010.h"
+#include "qt1010_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "QT1010: " args); printk("\n"); }} while (0)
+
+/* read single register */
+static int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val)
+{
+	struct i2c_msg msg[2] = {
+		{ .addr = priv->cfg->i2c_address, .flags = 0,        .buf = &reg, .len = 1 },
+		{ .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val,  .len = 1 },
+	};
+
+	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+		printk(KERN_WARNING "qt1010 I2C read failed\n");
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+/* write single register */
+static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val)
+{
+	u8 buf[2] = { reg, val };
+	struct i2c_msg msg = {
+		.addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2
+	};
+
+	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+		printk(KERN_WARNING "qt1010 I2C write failed\n");
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+/* dump all registers */
+static void qt1010_dump_regs(struct qt1010_priv *priv)
+{
+	char buf[52], buf2[4];
+	u8 reg, val;
+
+	for (reg = 0; ; reg++) {
+		if (reg % 16 == 0) {
+			if (reg)
+				printk("%s\n", buf);
+			sprintf(buf, "%02x: ", reg);
+		}
+		if (qt1010_readreg(priv, reg, &val) == 0)
+			sprintf(buf2, "%02x ", val);
+		else
+			strcpy(buf2, "-- ");
+		strcat(buf, buf2);
+		if (reg == 0x2f)
+			break;
+	}
+	printk("%s\n", buf);
+}
+
+static int qt1010_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct qt1010_priv *priv;
+	int err;
+	u32 freq, div, mod1, mod2;
+	u8 i, tmpval, reg05;
+	qt1010_i2c_oper_t rd[48] = {
+		{ QT1010_WR, 0x01, 0x80 },
+		{ QT1010_WR, 0x02, 0x3f },
+		{ QT1010_WR, 0x05, 0xff }, /* 02 c write */
+		{ QT1010_WR, 0x06, 0x44 },
+		{ QT1010_WR, 0x07, 0xff }, /* 04 c write */
+		{ QT1010_WR, 0x08, 0x08 },
+		{ QT1010_WR, 0x09, 0xff }, /* 06 c write */
+		{ QT1010_WR, 0x0a, 0xff }, /* 07 c write */
+		{ QT1010_WR, 0x0b, 0xff }, /* 08 c write */
+		{ QT1010_WR, 0x0c, 0xe1 },
+		{ QT1010_WR, 0x1a, 0xff }, /* 10 c write */
+		{ QT1010_WR, 0x1b, 0x00 },
+		{ QT1010_WR, 0x1c, 0x89 },
+		{ QT1010_WR, 0x11, 0xff }, /* 13 c write */
+		{ QT1010_WR, 0x12, 0xff }, /* 14 c write */
+		{ QT1010_WR, 0x22, 0xff }, /* 15 c write */
+		{ QT1010_WR, 0x1e, 0x00 },
+		{ QT1010_WR, 0x1e, 0xd0 },
+		{ QT1010_RD, 0x22, 0xff }, /* 16 c read */
+		{ QT1010_WR, 0x1e, 0x00 },
+		{ QT1010_RD, 0x05, 0xff }, /* 20 c read */
+		{ QT1010_RD, 0x22, 0xff }, /* 21 c read */
+		{ QT1010_WR, 0x23, 0xd0 },
+		{ QT1010_WR, 0x1e, 0x00 },
+		{ QT1010_WR, 0x1e, 0xe0 },
+		{ QT1010_RD, 0x23, 0xff }, /* 25 c read */
+		{ QT1010_RD, 0x23, 0xff }, /* 26 c read */
+		{ QT1010_WR, 0x1e, 0x00 },
+		{ QT1010_WR, 0x24, 0xd0 },
+		{ QT1010_WR, 0x1e, 0x00 },
+		{ QT1010_WR, 0x1e, 0xf0 },
+		{ QT1010_RD, 0x24, 0xff }, /* 31 c read */
+		{ QT1010_WR, 0x1e, 0x00 }, 
+		{ QT1010_WR, 0x14, 0x7f },
+		{ QT1010_WR, 0x15, 0x7f },
+		{ QT1010_WR, 0x05, 0xff }, /* 35 c write */
+		{ QT1010_WR, 0x06, 0x00 },
+		{ QT1010_WR, 0x15, 0x1f },
+		{ QT1010_WR, 0x16, 0xff },
+		{ QT1010_WR, 0x18, 0xff },
+		{ QT1010_WR, 0x1f, 0xff }, /* 40 c write */
+		{ QT1010_WR, 0x20, 0xff }, /* 41 c write */
+		{ QT1010_WR, 0x21, 0x53 },
+		{ QT1010_WR, 0x25, 0xff }, /* 43 c write */
+		{ QT1010_WR, 0x26, 0x15 },
+		{ QT1010_WR, 0x00, 0xff }, /* 45 c write */
+		{ QT1010_WR, 0x02, 0x00 },
+		{ QT1010_WR, 0x01, 0x00 }
+	};
+
+#define FREQ1 32000000 /* 32 MHz */
+#define FREQ2  4000000 /* 4 MHz Quartz oscillator in the stick? */
+
+	priv = fe->tuner_priv;
+	freq = params->frequency;
+	div = (freq + QT1010_OFFSET) / QT1010_STEP;
+	freq = (div * QT1010_STEP) - QT1010_OFFSET;
+	mod1 = (freq + QT1010_OFFSET) % FREQ1;
+	mod2 = (freq + QT1010_OFFSET) % FREQ2;
+	priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+	priv->frequency = freq;
+
+	/* reg 05 base value */
+	if      (freq < 290000000) reg05 = 0x14; /* 290 MHz */
+	else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */
+	else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */
+	else                       reg05 = 0x74;
+
+	/* 0x5 */
+	rd[2].val = reg05;
+
+	/* 07 - set frequency: 32 MHz scale */
+	rd[4].val = (freq + QT1010_OFFSET) / FREQ1;
+
+	/* 09 - changes every 8/24 MHz */
+	if (mod1 < 8000000) rd[6].val = 0x1d;
+	else                rd[6].val = 0x1c;
+
+	/* 0a - set frequency: 4 MHz scale (max 28 MHz) */
+	if      (mod1 < 1*FREQ2) rd[7].val = 0x09; /*  +0 MHz */
+	else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /*  +4 MHz */
+	else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /*  +8 MHz */
+	else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */
+	else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */
+	else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */
+	else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */
+	else                     rd[7].val = 0x0a; /* +28 MHz */
+
+	/* 0b - changes every 2/2 MHz */
+	if (mod2 < 2000000) rd[8].val = 0x45;
+	else                rd[8].val = 0x44;
+
+	/* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/
+	tmpval = 0x78; /* byte, overflows intentionally */
+	rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08);
+
+	/* 11 */
+	rd[13].val = 0xfd; /* TODO: correct value calculation */
+
+	/* 12 */
+	rd[14].val = 0x91; /* TODO: correct value calculation */
+
+	/* 22 */
+	if      (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */
+	else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */
+	else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */
+	else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */
+	else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */
+	else                       rd[15].val = 0xd0;
+
+	/* 05 */
+	rd[35].val = (reg05 & 0xf0);
+
+	/* 1f */
+	if      (mod1 <  8000000) tmpval = 0x00;
+	else if (mod1 < 12000000) tmpval = 0x01;
+	else if (mod1 < 16000000) tmpval = 0x02;
+	else if (mod1 < 24000000) tmpval = 0x03;
+	else if (mod1 < 28000000) tmpval = 0x04;
+	else                      tmpval = 0x05;
+	rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval);
+
+	/* 20 */
+	if      (mod1 <  8000000) tmpval = 0x00;
+	else if (mod1 < 12000000) tmpval = 0x01;
+	else if (mod1 < 20000000) tmpval = 0x02;
+	else if (mod1 < 24000000) tmpval = 0x03;
+	else if (mod1 < 28000000) tmpval = 0x04;
+	else                      tmpval = 0x05;
+	rd[41].val = (priv->reg20_init_val + 0x0d + tmpval);
+
+	/* 25 */
+	rd[43].val = priv->reg25_init_val;
+
+	/* 00 */
+	rd[45].val = 0x92; /* TODO: correct value calculation */
+
+	dprintk("freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x 1a:%02x 11:%02x " \
+	        "12:%02x 22:%02x 05:%02x 1f:%02x 20:%02x 25:%02x 00:%02x", \
+	        freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, rd[8].val, \
+	        rd[10].val, rd[13].val, rd[14].val, rd[15].val, rd[35].val, \
+	        rd[40].val, rd[41].val, rd[43].val, rd[45].val);
+
+	for (i = 0; i < sizeof(rd) / sizeof(*rd); i++) {
+		if (rd[i].oper == QT1010_WR) {
+			err = qt1010_writereg(priv, rd[i].reg, rd[i].val);
+		} else { /* read is required to proper locking */
+			err = qt1010_readreg(priv, rd[i].reg, &tmpval);
+		}
+		if (err) return err;
+	}
+
+	if (debug)
+		qt1010_dump_regs(priv);
+
+	return 0;
+}
+
+static int qt1010_init_meas1(struct qt1010_priv *priv, u8 oper, u8 reg, u8 reg_init_val, u8 *retval)
+{
+	u8 i, val1, val2;
+	int err;
+
+	qt1010_i2c_oper_t i2c_data[] = {
+		{ QT1010_WR, reg, reg_init_val },
+		{ QT1010_WR, 0x1e, 0x00 },
+		{ QT1010_WR, 0x1e, oper },
+		{ QT1010_RD, reg, 0xff }
+	};
+
+	for (i = 0; i < sizeof(i2c_data) / sizeof(*i2c_data); i++) {
+		if (i2c_data[i].oper == QT1010_WR) {
+			err = qt1010_writereg(priv, i2c_data[i].reg, i2c_data[i].val);
+		} else {
+			err = qt1010_readreg(priv, i2c_data[i].reg, &val2);
+		}
+		if (err) return err;
+	}
+
+	do {
+		val1 = val2;
+		err = qt1010_readreg(priv, reg, &val2);
+		if (err) return err;
+		dprintk("compare reg:%02x %02x %02x", reg, val1, val2);
+	} while (val1 != val2);
+	*retval = val1;
+
+	return qt1010_writereg(priv, 0x1e, 0x00);
+}
+
+
+static u8 qt1010_init_meas2(struct qt1010_priv *priv, u8 reg_init_val, u8 *retval)
+{
+	u8 i, val;
+	int err;
+	qt1010_i2c_oper_t i2c_data[] = {
+		{ QT1010_WR, 0x07, reg_init_val },
+		{ QT1010_WR, 0x22, 0xd0 },
+		{ QT1010_WR, 0x1e, 0x00 },
+		{ QT1010_WR, 0x1e, 0xd0 },
+		{ QT1010_RD, 0x22, 0xff },
+		{ QT1010_WR, 0x1e, 0x00 },
+		{ QT1010_WR, 0x22, 0xff }
+	};
+	for (i = 0; i < sizeof(i2c_data) / sizeof(*i2c_data); i++) {
+		if (i2c_data[i].oper == QT1010_WR) {
+			err = qt1010_writereg(priv, i2c_data[i].reg, i2c_data[i].val);
+		} else {
+			err = qt1010_readreg(priv, i2c_data[i].reg, &val);
+		}
+		if (err) return err;
+	}
+	*retval = val;
+	return 0;
+}
+
+static int qt1010_init(struct dvb_frontend *fe)
+{
+	struct qt1010_priv *priv = fe->tuner_priv;
+	struct dvb_frontend_parameters params;
+	int err;
+	u8 i, tmpval, *valptr = NULL;
+
+	qt1010_i2c_oper_t i2c_data[] = {
+		{ QT1010_WR, 0x01, 0x80 },
+		{ QT1010_WR, 0x0d, 0x84 },
+		{ QT1010_WR, 0x0e, 0xb7 },
+		{ QT1010_WR, 0x2a, 0x23 },
+		{ QT1010_WR, 0x2c, 0xdc },
+		{ QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */
+		{ QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */
+		{ QT1010_WR, 0x2b, 0x70 },
+		{ QT1010_WR, 0x2a, 0x23 },
+		{ QT1010_M1, 0x26, 0x08 },
+		{ QT1010_M1, 0x82, 0xff },
+		{ QT1010_WR, 0x05, 0x14 },
+		{ QT1010_WR, 0x06, 0x44 },
+		{ QT1010_WR, 0x07, 0x28 },
+		{ QT1010_WR, 0x08, 0x0b },
+		{ QT1010_WR, 0x11, 0xfd },
+		{ QT1010_M1, 0x22, 0x0d },
+		{ QT1010_M1, 0xd0, 0xff },
+		{ QT1010_WR, 0x06, 0x40 },
+		{ QT1010_WR, 0x16, 0xf0 },
+		{ QT1010_WR, 0x02, 0x38 },
+		{ QT1010_WR, 0x03, 0x18 },
+		{ QT1010_WR, 0x20, 0xe0 },
+		{ QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */
+		{ QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */
+		{ QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */
+		{ QT1010_WR, 0x03, 0x19 },
+		{ QT1010_WR, 0x02, 0x3f },
+		{ QT1010_WR, 0x21, 0x53 },
+		{ QT1010_RD, 0x21, 0xff },
+		{ QT1010_WR, 0x11, 0xfd },
+		{ QT1010_WR, 0x05, 0x34 },
+		{ QT1010_WR, 0x06, 0x44 },
+		{ QT1010_WR, 0x08, 0x08 }
+	};
+
+	for (i = 0; i < sizeof(i2c_data) / sizeof(*i2c_data); i++) {
+		switch (i2c_data[i].oper) {
+		case QT1010_WR:
+			err = qt1010_writereg(priv, i2c_data[i].reg, i2c_data[i].val);
+			break;
+		case QT1010_RD:
+			if (i2c_data[i].val == 0x20) valptr = &priv->reg20_init_val;
+			else valptr = &tmpval;
+			err = qt1010_readreg(priv, i2c_data[i].reg, valptr);
+			break;
+		case QT1010_M1:
+			if (i2c_data[i].val == 0x25) valptr = &priv->reg25_init_val;
+			else if (i2c_data[i].val == 0x1f) valptr = &priv->reg1f_init_val;
+			else valptr = &tmpval;
+			err = qt1010_init_meas1(priv, i2c_data[i+1].reg, i2c_data[i].reg,
+			                              i2c_data[i].val, valptr);
+			i++;
+			break;
+		}
+		if (err) return err;
+	}
+
+	for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */
+		if ((err = qt1010_init_meas2(priv, i, &tmpval)))
+			return err;
+
+	params.frequency = 545000000; /* Sigmatek DVB-110 545000000 */
+	                              /* MSI Megasky 580 GL861 533000000 */
+	return qt1010_set_params(fe, &params);
+}
+
+static int qt1010_release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct qt1010_priv *priv = fe->tuner_priv;
+	*frequency = priv->frequency;
+	return 0;
+}
+
+static int qt1010_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+	struct qt1010_priv *priv = fe->tuner_priv;
+	*bandwidth = priv->bandwidth;
+	return 0;
+}
+
+static const struct dvb_tuner_ops qt1010_tuner_ops = {
+	.info = {
+		.name           = "Quantek QT1010",
+		.frequency_min  = QT1010_MIN_FREQ,
+		.frequency_max  = QT1010_MAX_FREQ,
+		.frequency_step = QT1010_STEP,
+	},
+
+	.release       = qt1010_release,
+	.init          = qt1010_init,
+	/* TODO: implement sleep */
+
+	.set_params    = qt1010_set_params,
+	.get_frequency = qt1010_get_frequency,
+	.get_bandwidth = qt1010_get_bandwidth
+};
+
+struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe,
+                                    struct i2c_adapter *i2c,
+                                    struct qt1010_config *cfg)
+{
+	struct qt1010_priv *priv = NULL;
+	u8 id;
+
+	priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+
+	priv->cfg      = cfg;
+	priv->i2c      = i2c;
+
+#if 0
+	qt1010_dump_regs(priv);
+#endif
+
+	/* Try to detect tuner chip. Probably this is not correct register. */
+	if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) {
+		kfree(priv);
+		return NULL;
+	}
+
+	printk(KERN_INFO "Quantek QT1010 successfully identified.\n");
+	memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops, sizeof(struct dvb_tuner_ops));
+
+	fe->tuner_priv = priv;
+	return fe;
+}
+EXPORT_SYMBOL(qt1010_attach);
+
+MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@xxxxxx>");
+MODULE_AUTHOR("Aapo Tahkola <aet@xxxxxxxxxxxxxx>");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff -r 11fa5f80d049 linux/drivers/media/dvb/frontends/qt1010_priv.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/qt1010_priv.h	Tue Dec 12 23:33:08 2006 +0200
@@ -0,0 +1,105 @@
+/*
+ *  Driver for Quantek QT1010 silicon tuner
+ *
+ *  Copyright (C) 2006 Antti Palosaari <crope@xxxxxx>
+ *                     Aapo Tahkola <aet@xxxxxxxxxxxxxx>
+ *
+ *  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 QT1010_PRIV_H
+#define QT1010_PRIV_H
+
+/*
+reg def meaning
+=== === =======
+00  00  ?
+01  a0  ? operation start/stop; start=80, stop=00
+02  00  ?
+03  19  ?
+04  00  ?
+05  00  ? maybe band selection
+06  00  ?
+07  2b  set frequency: 32 MHz scale, n*32 MHz
+08  0b  ?
+09  10  ? changes every 8/24 MHz; values 1d/1c
+0a  08  set frequency: 4 MHz scale, n*4 MHz
+0b  41  ? changes every 2/2 MHz; values 45/45
+0c  e1  ?
+0d  94  ?
+0e  b6  ?
+0f  2c  ?
+10  10  ?
+11  f1  ? maybe device specified adjustment
+12  11  ? maybe device specified adjustment
+13  3f  ?
+14  1f  ?
+15  3f  ?
+16  ff  ?
+17  ff  ?
+18  f7  ?
+19  80  ?
+1a  d0  set frequency: 125 kHz scale, n*125 kHz
+1b  00  ?
+1c  89  ?
+1d  00  ?
+1e  00  ? looks like operation register; write cmd here, read result from 1f-26
+1f  20  ? chip initialization
+20  e0  ? chip initialization
+21  20  ?
+22  d0  ?
+23  d0  ?
+24  d0  ?
+25  40  ? chip initialization
+26  08  ?
+27  29  ?
+28  55  ?
+29  39  ?
+2a  13  ?
+2b  01  ?
+2c  ea  ?
+2d  00  ?
+2e  00  ? not used?
+2f  00  ? not used?
+*/
+
+#define QT1010_STEP         125000 /*  125 kHz used by Windows drivers,
+                                      hw could be more precise but we don't
+                                      know how to use */
+#define QT1010_MIN_FREQ   48000000 /*   48 MHz */
+#define QT1010_MAX_FREQ  860000000 /*  860 MHz */
+#define QT1010_OFFSET   1246000000 /* 1246 MHz */
+
+#define QT1010_WR 0
+#define QT1010_RD 1
+#define QT1010_M1 3
+
+typedef struct {
+	u8 oper, reg, val;
+} qt1010_i2c_oper_t;
+
+struct qt1010_priv {
+	struct qt1010_config *cfg;
+	struct i2c_adapter   *i2c;
+
+	u8 reg1f_init_val;
+	u8 reg20_init_val;
+	u8 reg25_init_val;
+
+	u32 frequency;
+	u32 bandwidth;
+};
+
+#endif
_______________________________________________
linux-dvb mailing list
linux-dvb@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb

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

  Powered by Linux