All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michael Büsch" <m@bues.ch>
To: Hans-Frieder Vogt <hfvogt@gmx.net>
Cc: gennarone@gmail.com, linux-media@vger.kernel.org,
	Benjamin Larsson <benjamin@southpole.se>
Subject: Add fc0011 tuner support (was: [PATCH 2/3] Basic AF9035/AF9033 driver)
Date: Tue, 20 Mar 2012 22:44:38 +0100	[thread overview]
Message-ID: <20120320224438.73ac5d5c@milhouse> (raw)
In-Reply-To: <20120320183250.504fcced@milhouse>


[-- Attachment #1.1: Type: text/plain, Size: 16385 bytes --]

Okay, here we go. These patches apply on top of the latest
af903x by Hans-Frieder Vogt. This adds support for the fc0011
tuner, which is used on a "cabstone" stick. (Its USB-id is already
in af903x's list).

I did some _very_ basic testing on this stuff and it basically seems to
work more or less.

Signed-off-by: Michael Buesch <m@bues.ch>

---

Two patches for review and testing follow (inline for commenting and attached).
(Yes, the '#if 0' sections are going to be removed. I'm currently
testing on a 3.2 kernel, so I need them for now.)

---

Index: linux-source-3.2/drivers/media/common/tuners/Kconfig
===================================================================
--- linux-source-3.2.orig/drivers/media/common/tuners/Kconfig	2012-03-20 20:34:00.938801738 +0100
+++ linux-source-3.2/drivers/media/common/tuners/Kconfig	2012-03-20 20:34:00.986802601 +0100
@@ -204,6 +204,13 @@
 	help
 	  NXP TDA18212 silicon tuner driver.
 
+config MEDIA_TUNER_FC0011
+	tristate "Fitipower FC0011 silicon tuner"
+	depends on VIDEO_MEDIA && I2C
+	default m if MEDIA_TUNER_CUSTOMISE
+	help
+	  Fitipower FC0011 silicon tuner driver.
+
 config MEDIA_TUNER_FC0012
 	tristate "Fitipower FC0012 silicon tuner"
 	depends on VIDEO_MEDIA && I2C
Index: linux-source-3.2/drivers/media/common/tuners/Makefile
===================================================================
--- linux-source-3.2.orig/drivers/media/common/tuners/Makefile	2012-03-20 20:34:00.942801805 +0100
+++ linux-source-3.2/drivers/media/common/tuners/Makefile	2012-03-20 20:34:00.986802601 +0100
@@ -27,6 +27,7 @@
 obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
 obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
 obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
+obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
 obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o
 
 ccflags-y += -Idrivers/media/dvb/dvb-core
Index: linux-source-3.2/drivers/media/common/tuners/fc0011.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-source-3.2/drivers/media/common/tuners/fc0011.c	2012-03-20 22:28:54.132458133 +0100
@@ -0,0 +1,386 @@
+/*
+ * Fitipower FC0011 tuner driver
+ *
+ * Copyright (C) 2012 Michael Buesch <m@bues.ch>
+ *
+ * Derived from FC0012 tuner driver:
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ *
+ * 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 "fc0011.h"
+
+
+struct fc0011_priv {
+        struct i2c_adapter *i2c;
+	u8 addr;
+
+        u32 frequency;
+	u32 bandwidth;
+};
+
+
+static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val)
+{
+	u8 buf[2] = { reg, val };
+	struct i2c_msg msg = { .addr = priv->addr,
+		.flags = 0, .buf = buf, .len = 2 };
+
+	msleep(1);
+	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+		dev_err(&priv->i2c->dev,
+			"I2C write reg failed, reg: %02x, val: %02x\n",
+			reg, val);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val)
+{
+	struct i2c_msg msg[2] = {
+		{ .addr = priv->addr,
+		  .flags = 0, .buf = &reg, .len = 1 },
+		{ .addr = priv->addr,
+		  .flags = I2C_M_RD, .buf = val, .len = 1 },
+	};
+
+	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+		dev_err(&priv->i2c->dev,
+			"I2C read failed, reg: %02x\n", reg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int fc0011_release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static int fc0011_init(struct dvb_frontend *fe)
+{
+	/* nothing to do here */
+	return 0;
+}
+
+static int fc0011_sleep(struct dvb_frontend *fe)
+{
+	/* nothing to do here */
+	return 0;
+}
+
+#if 0 //TODO 3.3
+static int fc0011_set_params(struct dvb_frontend *fe)
+#else
+static int fc0011_set_params(struct dvb_frontend *fe,
+        struct dvb_frontend_parameters *params)
+#endif
+{
+        struct fc0011_priv *priv = fe->tuner_priv;
+	int err;
+	unsigned int i;
+#if 0 //TODO 3.3
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	u32 freq = p->frequency / 1000;
+	u32 delsys = p->delivery_system;
+	u32 bandwidth = p->bandwidth_hz / 1000;
+#else
+	u32 freq = params->frequency / 1000;
+	u32 bandwidth = params->u.ofdm.bandwidth / 1000;
+#endif
+	u32 fvco, xin, xdiv, xdivr, fa;
+	u16 frac;
+	u8 fp, vco_sel, vco_cal;
+	u8 regs[14] = { };
+
+	err = fc0011_writereg(priv, 7, 0x0F);
+	err |= fc0011_writereg(priv, 8, 0x3E);
+	err |= fc0011_writereg(priv, 10, 0xB8);
+	err |= fc0011_writereg(priv, 11, 0x80);
+	err |= fc0011_writereg(priv, 13, 0x04);
+	if (err)
+		return -EIO;
+
+	/* Set VCO freq and VCO div */
+	if (freq < 54000) {
+		fvco = freq * 64;
+		regs[5] = 0x82;
+	} else if (freq < 108000) {
+		fvco = freq * 32;
+		regs[5] = 0x42;
+	} else if (freq < 216000) {
+		fvco = freq * 16;
+		regs[5] = 0x22;
+	} else if (freq < 432000) {
+		fvco = freq * 8;
+		regs[5] = 0x12;
+	} else {
+		fvco = freq * 4;
+		regs[5] = 0x0A;
+	}
+
+	/* Calc XIN */
+	xdiv = fvco / 18000;
+	frac = fvco - xdiv * 18000;
+	frac = (frac << 15) / 18000;
+	if (frac >= 16384)
+		frac += 32786;
+	if (!frac)
+		xin = 0;
+	else if (frac < 511)
+		xin = 0x200;
+	else if (frac < 65025)
+		xin = frac;
+	else
+		xin = 0xFE00;
+	regs[3] = xin >> 8;
+	regs[4] = xin;
+	err = fc0011_writereg(priv, 3, regs[3]);
+	err |= fc0011_writereg(priv, 4, regs[4]);
+	if (err)
+		return -EIO;
+
+	/* Calc FP and FA */
+	xdivr = xdiv;
+	if (fvco - xdiv * 18000 >= 9000)
+		xdivr = xdiv + 1;
+	fp = xdivr / 8;
+	fa = xdivr - fp * 8;
+	if (fa < 2) {
+		fp -= 1;
+		fa += 8;
+	}
+	regs[1] = fa;
+	regs[2] = fp;
+	err = fc0011_writereg(priv, 1, regs[1]);
+	err |= fc0011_writereg(priv, 2, regs[2]);
+	if (err)
+		return -EIO;
+
+	/* Select bandwidth */
+	switch (bandwidth) {
+	default:
+	case 8000:
+		regs[6] = 0;
+		break;
+	case 7000:
+		regs[6] = 0x40;
+		break;
+	case 6000:
+		regs[6] = 0x80;
+		break;
+	}
+
+	/* Pre VCO select */
+	if (fvco < 2320000) {
+		vco_sel = 0;
+		regs[6] &= 0xE7;
+	} else if (fvco < 3080000) {
+		vco_sel = 1;
+		regs[6] |= 0x10;
+	} else {
+		vco_sel = 2;//FIXME?
+		regs[6] |= 0x8;
+	}
+
+	/* Fix for low freqs */
+	if (freq < 45000) {
+		regs[1] = 0x6;
+		regs[2] = 0x11;
+	}
+
+	/* Clock out fix */
+	regs[6] |= 0x20;
+
+	/* Write the cached registers */
+	for (i = 1; i < 7; i++) {
+		err = fc0011_writereg(priv, i, regs[i]);
+		if (err)
+			return err;
+	}
+
+	/* VCO calibration */
+	err = fc0011_writereg(priv, 14, 0x80); /* reset */
+	if (err)
+		return err;
+	err = fc0011_writereg(priv, 14, 0); /* run */
+	if (err)
+		return err;
+	/* Read calib val */
+	err = fc0011_writereg(priv, 14, 0);
+	if (err)
+		return err;
+	msleep(10);
+	err = fc0011_readreg(priv, 14, &vco_cal);
+	if (err)
+		return err;
+	vco_cal &= 0x3F;
+
+	switch (vco_sel) {
+	case 0:
+		if (vco_cal < 8) {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x10 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			err |= fc0011_writereg(priv, 14, 0x80);
+			err |= fc0011_writereg(priv, 14, 0);
+			if (err)
+				return -EIO;
+		} else {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			if (err)
+				return err;
+		}
+		break;
+	case 1:
+		if (vco_cal < 5) {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x8 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			if (err)
+				return err;
+		} else if (vco_cal < 48) {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x10 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			if (err)
+				return err;
+		} else {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			err |= fc0011_writereg(priv, 14, 0x80);
+			err |= fc0011_writereg(priv, 14, 0);
+			if (err)
+				return -EIO;
+		}
+		break;
+	case 2:
+		if (vco_cal > 53) {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x10 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			err |= fc0011_writereg(priv, 14, 0x80);
+			err |= fc0011_writereg(priv, 14, 0);
+			if (err)
+				return -EIO;
+		} else {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x8 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			if (err)
+				return err;
+		}
+		break;
+	}
+	msleep(10);
+
+	err = fc0011_readreg(priv, 13, &regs[13]);
+	if (err)
+		return err;
+	regs[13] |= 0x10;
+	err = fc0011_writereg(priv, 13, regs[13]);
+	if (err)
+		return err;
+	err = fc0011_writereg(priv, 16, 0xB);
+	if (err)
+		return err;
+
+	priv->frequency = freq * 1000;
+	priv->bandwidth = bandwidth * 1000;
+
+	return 0;
+}
+
+static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+        struct fc0011_priv *priv = fe->tuner_priv;
+
+        *frequency = priv->frequency;
+
+        return 0;
+}
+
+static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+        *frequency = 0;
+        return 0;
+}
+
+static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+        struct fc0011_priv *priv = fe->tuner_priv;
+
+        *bandwidth = priv->bandwidth;
+
+        return 0;
+}
+
+static const struct dvb_tuner_ops fc0011_tuner_ops = {
+	.info = {
+		.name		= "Fitipower FC0011",
+
+		.frequency_min	= 170000000, //FIXME?
+		.frequency_max	= 860000000, //FIXME?
+		.frequency_step	= 0,
+        },
+
+	.release		= fc0011_release,
+
+	.init			= fc0011_init,
+	.sleep			= fc0011_sleep,
+
+	.set_params		= fc0011_set_params,
+
+	.get_frequency		= fc0011_get_frequency,
+	.get_if_frequency	= fc0011_get_if_frequency,
+	.get_bandwidth		= fc0011_get_bandwidth,
+};
+
+struct dvb_frontend * fc0011_attach(struct dvb_frontend *fe,
+				    struct i2c_adapter *i2c,
+				    u8 i2c_address)
+{
+        struct fc0011_priv *priv = NULL;
+
+	priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->i2c = i2c;
+	priv->addr = i2c_address;
+
+	dev_info(&priv->i2c->dev, "Fitipower FC0011 attached\n");
+
+	fe->tuner_priv = priv;
+
+	memcpy(&fe->ops.tuner_ops, &fc0011_tuner_ops,
+	       sizeof(struct dvb_tuner_ops));
+
+	return fe;
+}
+EXPORT_SYMBOL(fc0011_attach);
+
+MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
+MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
+MODULE_LICENSE("GPL");
Index: linux-source-3.2/drivers/media/common/tuners/fc0011.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-source-3.2/drivers/media/common/tuners/fc0011.h	2012-03-20 20:34:00.990802668 +0100
@@ -0,0 +1,22 @@
+#ifndef LINUX_FC0011_H_
+#define LINUX_FC0011_H_
+
+#include "dvb_frontend.h"
+
+#if defined(CONFIG_MEDIA_TUNER_FC0011) ||\
+    defined(CONFIG_MEDIA_TUNER_FC0011_MODULE)
+struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
+				   struct i2c_adapter *i2c,
+				   u8 i2c_address);
+#else
+static inline
+struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
+				   struct i2c_adapter *i2c,
+				   u8 i2c_address)
+{
+	dev_err(&i2c->dev, "Driver disabled in Kconfig\n");
+	return NULL;
+}
+#endif
+
+#endif /* LINUX_FC0011_H_ */




Index: linux-source-3.2/drivers/media/dvb/dvb-usb/Kconfig
===================================================================
--- linux-source-3.2.orig/drivers/media/dvb/dvb-usb/Kconfig	2012-03-20 22:27:53.823367115 +0100
+++ linux-source-3.2/drivers/media/dvb/dvb-usb/Kconfig	2012-03-20 22:29:13.124801711 +0100
@@ -342,6 +342,7 @@
 config DVB_USB_AF903X
 	tristate "Afatech AF903X DVB-T USB2.0 support"
 	depends on DVB_USB && EXPERIMENTAL
+	select MEDIA_TUNER_FC0011   if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_FC0012   if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
 	help
Index: linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-devices.c
===================================================================
--- linux-source-3.2.orig/drivers/media/dvb/dvb-usb/af903x-devices.c	2012-03-20 22:27:53.823367115 +0100
+++ linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-devices.c	2012-03-20 22:29:13.128801781 +0100
@@ -260,6 +260,7 @@
 }
 
 extern struct tuner_desc tuner_af9007;
+extern struct tuner_desc tuner_fc0011;
 extern struct tuner_desc tuner_fc0012;
 extern struct tuner_desc tuner_mxl5007t;
 
@@ -273,6 +274,9 @@
 	case TUNER_AF9007:
 		ctx->tuner_desc = &tuner_af9007;
 		break;
+	case TUNER_FC0011:
+		ctx->tuner_desc = &tuner_fc0011;
+		break;
 	case TUNER_FC0012:
 		ctx->tuner_desc = &tuner_fc0012;
 		break;
Index: linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-fe.c
===================================================================
--- linux-source-3.2.orig/drivers/media/dvb/dvb-usb/af903x-fe.c	2012-03-20 22:27:53.823367115 +0100
+++ linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-fe.c	2012-03-20 22:29:13.132801864 +0100
@@ -28,6 +28,7 @@
 #include "af903x-fe.h"
 #include "af903x-fe-priv.h"
 #include "dvb_frontend.h"
+#include "fc0011.h"
 #include "fc0012.h"
 #include "mxl5007t.h"
 
@@ -1990,6 +1991,12 @@
 	switch(ctx->tuner_desc->tunerId) {
 	case TUNER_AF9007:
 		break;
+	case TUNER_FC0011:
+		ret = dvb_attach(fc0011_attach, frontend, i2c_adap,
+			id == 0 ? ctx->tuner_desc->tuner_addr :
+			ctx->tuner_desc->tuner_addr + 1)
+			== NULL ?  -ENODEV : 0;
+		break;
 	case TUNER_FC0012:
 		ret = dvb_attach(fc0012_attach, frontend, i2c_adap,
 			id == 0 ? ctx->tuner_desc->tuner_addr :
Index: linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-tuners.c
===================================================================
--- linux-source-3.2.orig/drivers/media/dvb/dvb-usb/af903x-tuners.c	2012-03-20 22:27:53.823367115 +0100
+++ linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-tuners.c	2012-03-20 22:29:13.136801932 +0100
@@ -189,6 +189,69 @@
 	{0xf1e6, 0x00},
 };
 
+static u16 fc0011_script_sets[] = {
+	0x38,
+};
+
+static struct af903x_val_set fc0011_scripts[] = {
+	{0x0046, 0x28}, /* TUNER_ID */
+	{0x0057, 0x00},
+	{0x0058, 0x01},
+	{0x005f, 0x00},
+	{0x0060, 0x00},
+	{0x0068, 0xa5},
+	{0x006e, 0x01},
+	{0x0071, 0x0A},
+	{0x0072, 0x02},
+	{0x0074, 0x01},
+	{0x0079, 0x01},
+	{0x0093, 0x00},
+	{0x0094, 0x00},
+	{0x0095, 0x00},
+	{0x0096, 0x00},
+	{0x009b, 0x2D},
+	{0x009c, 0x60},
+	{0x009d, 0x23},
+	{0x00a4, 0x50},
+	{0x00ad, 0x50},
+	{0x00b3, 0x01},
+	{0x00b7, 0x88},
+	{0x00b8, 0xa6},
+	{0x00c3, 0x01},
+	{0x00c4, 0x01},
+	{0x00c7, 0x69},
+	{0xF007, 0x00},
+	{0xF00A, 0x1B},
+	{0xF00B, 0x1B},
+	{0xF00C, 0x1B},
+	{0xF00D, 0x1B},
+	{0xF00E, 0xFF},
+	{0xF00F, 0x01},
+	{0xF010, 0x00},
+	{0xF011, 0x02},
+	{0xF012, 0xFF},
+	{0xF013, 0x01},
+	{0xF014, 0x00},
+	{0xF015, 0x02},
+	{0xF01B, 0xEF},
+	{0xF01C, 0x01},
+	{0xF01D, 0x0f},
+	{0xF01E, 0x02},
+	{0xF01F, 0x6E},
+	{0xF020, 0x00},
+	{0xF025, 0xDE},
+	{0xF026, 0x00},
+	{0xF027, 0x0A},
+	{0xF028, 0x03},
+	{0xF029, 0x6E},
+	{0xF02A, 0x00},
+	{0xF047, 0x00},
+	{0xF054, 0x0},
+	{0xF055, 0x0},
+	{0xF077, 0x01},
+	{0xF1E6, 0x00},
+};
+
 static int af903x_fc0012_init(struct af903x_dev_ctx *ctx, int chip)
 {
 	int ret;
@@ -338,6 +401,19 @@
 	TUNER_FC0012,
 };
 
+struct tuner_desc tuner_fc0011 = {
+	af903x_fc0012_init,
+	af903x_fc0012_sleep,
+	af903x_fc0012_set,
+	fc0011_scripts,
+	fc0011_script_sets,
+	0xc0,
+	1,
+	0,
+	true,
+	TUNER_FC0011,
+};
+
 static u16 mxl5007t_script_sets[] = {
 	0x1e
 };


-- 
Greetings, Michael.

PGP encryption is encouraged / 908D8B0E

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: fc0011-tuner.patch --]
[-- Type: text/x-patch, Size: 11443 bytes --]

Index: linux-source-3.2/drivers/media/common/tuners/Kconfig
===================================================================
--- linux-source-3.2.orig/drivers/media/common/tuners/Kconfig	2012-03-20 20:34:00.938801738 +0100
+++ linux-source-3.2/drivers/media/common/tuners/Kconfig	2012-03-20 20:34:00.986802601 +0100
@@ -204,6 +204,13 @@
 	help
 	  NXP TDA18212 silicon tuner driver.
 
+config MEDIA_TUNER_FC0011
+	tristate "Fitipower FC0011 silicon tuner"
+	depends on VIDEO_MEDIA && I2C
+	default m if MEDIA_TUNER_CUSTOMISE
+	help
+	  Fitipower FC0011 silicon tuner driver.
+
 config MEDIA_TUNER_FC0012
 	tristate "Fitipower FC0012 silicon tuner"
 	depends on VIDEO_MEDIA && I2C
Index: linux-source-3.2/drivers/media/common/tuners/Makefile
===================================================================
--- linux-source-3.2.orig/drivers/media/common/tuners/Makefile	2012-03-20 20:34:00.942801805 +0100
+++ linux-source-3.2/drivers/media/common/tuners/Makefile	2012-03-20 20:34:00.986802601 +0100
@@ -27,6 +27,7 @@
 obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
 obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
 obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
+obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
 obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o
 
 ccflags-y += -Idrivers/media/dvb/dvb-core
Index: linux-source-3.2/drivers/media/common/tuners/fc0011.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-source-3.2/drivers/media/common/tuners/fc0011.c	2012-03-20 22:28:54.132458133 +0100
@@ -0,0 +1,386 @@
+/*
+ * Fitipower FC0011 tuner driver
+ *
+ * Copyright (C) 2012 Michael Buesch <m@bues.ch>
+ *
+ * Derived from FC0012 tuner driver:
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ *
+ * 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 "fc0011.h"
+
+
+struct fc0011_priv {
+        struct i2c_adapter *i2c;
+	u8 addr;
+
+        u32 frequency;
+	u32 bandwidth;
+};
+
+
+static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val)
+{
+	u8 buf[2] = { reg, val };
+	struct i2c_msg msg = { .addr = priv->addr,
+		.flags = 0, .buf = buf, .len = 2 };
+
+	msleep(1);
+	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+		dev_err(&priv->i2c->dev,
+			"I2C write reg failed, reg: %02x, val: %02x\n",
+			reg, val);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val)
+{
+	struct i2c_msg msg[2] = {
+		{ .addr = priv->addr,
+		  .flags = 0, .buf = &reg, .len = 1 },
+		{ .addr = priv->addr,
+		  .flags = I2C_M_RD, .buf = val, .len = 1 },
+	};
+
+	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+		dev_err(&priv->i2c->dev,
+			"I2C read failed, reg: %02x\n", reg);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int fc0011_release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static int fc0011_init(struct dvb_frontend *fe)
+{
+	/* nothing to do here */
+	return 0;
+}
+
+static int fc0011_sleep(struct dvb_frontend *fe)
+{
+	/* nothing to do here */
+	return 0;
+}
+
+#if 0 //TODO 3.3
+static int fc0011_set_params(struct dvb_frontend *fe)
+#else
+static int fc0011_set_params(struct dvb_frontend *fe,
+        struct dvb_frontend_parameters *params)
+#endif
+{
+        struct fc0011_priv *priv = fe->tuner_priv;
+	int err;
+	unsigned int i;
+#if 0 //TODO 3.3
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	u32 freq = p->frequency / 1000;
+	u32 delsys = p->delivery_system;
+	u32 bandwidth = p->bandwidth_hz / 1000;
+#else
+	u32 freq = params->frequency / 1000;
+	u32 bandwidth = params->u.ofdm.bandwidth / 1000;
+#endif
+	u32 fvco, xin, xdiv, xdivr, fa;
+	u16 frac;
+	u8 fp, vco_sel, vco_cal;
+	u8 regs[14] = { };
+
+	err = fc0011_writereg(priv, 7, 0x0F);
+	err |= fc0011_writereg(priv, 8, 0x3E);
+	err |= fc0011_writereg(priv, 10, 0xB8);
+	err |= fc0011_writereg(priv, 11, 0x80);
+	err |= fc0011_writereg(priv, 13, 0x04);
+	if (err)
+		return -EIO;
+
+	/* Set VCO freq and VCO div */
+	if (freq < 54000) {
+		fvco = freq * 64;
+		regs[5] = 0x82;
+	} else if (freq < 108000) {
+		fvco = freq * 32;
+		regs[5] = 0x42;
+	} else if (freq < 216000) {
+		fvco = freq * 16;
+		regs[5] = 0x22;
+	} else if (freq < 432000) {
+		fvco = freq * 8;
+		regs[5] = 0x12;
+	} else {
+		fvco = freq * 4;
+		regs[5] = 0x0A;
+	}
+
+	/* Calc XIN */
+	xdiv = fvco / 18000;
+	frac = fvco - xdiv * 18000;
+	frac = (frac << 15) / 18000;
+	if (frac >= 16384)
+		frac += 32786;
+	if (!frac)
+		xin = 0;
+	else if (frac < 511)
+		xin = 0x200;
+	else if (frac < 65025)
+		xin = frac;
+	else
+		xin = 0xFE00;
+	regs[3] = xin >> 8;
+	regs[4] = xin;
+	err = fc0011_writereg(priv, 3, regs[3]);
+	err |= fc0011_writereg(priv, 4, regs[4]);
+	if (err)
+		return -EIO;
+
+	/* Calc FP and FA */
+	xdivr = xdiv;
+	if (fvco - xdiv * 18000 >= 9000)
+		xdivr = xdiv + 1;
+	fp = xdivr / 8;
+	fa = xdivr - fp * 8;
+	if (fa < 2) {
+		fp -= 1;
+		fa += 8;
+	}
+	regs[1] = fa;
+	regs[2] = fp;
+	err = fc0011_writereg(priv, 1, regs[1]);
+	err |= fc0011_writereg(priv, 2, regs[2]);
+	if (err)
+		return -EIO;
+
+	/* Select bandwidth */
+	switch (bandwidth) {
+	default:
+	case 8000:
+		regs[6] = 0;
+		break;
+	case 7000:
+		regs[6] = 0x40;
+		break;
+	case 6000:
+		regs[6] = 0x80;
+		break;
+	}
+
+	/* Pre VCO select */
+	if (fvco < 2320000) {
+		vco_sel = 0;
+		regs[6] &= 0xE7;
+	} else if (fvco < 3080000) {
+		vco_sel = 1;
+		regs[6] |= 0x10;
+	} else {
+		vco_sel = 2;//FIXME?
+		regs[6] |= 0x8;
+	}
+
+	/* Fix for low freqs */
+	if (freq < 45000) {
+		regs[1] = 0x6;
+		regs[2] = 0x11;
+	}
+
+	/* Clock out fix */
+	regs[6] |= 0x20;
+
+	/* Write the cached registers */
+	for (i = 1; i < 7; i++) {
+		err = fc0011_writereg(priv, i, regs[i]);
+		if (err)
+			return err;
+	}
+
+	/* VCO calibration */
+	err = fc0011_writereg(priv, 14, 0x80); /* reset */
+	if (err)
+		return err;
+	err = fc0011_writereg(priv, 14, 0); /* run */
+	if (err)
+		return err;
+	/* Read calib val */
+	err = fc0011_writereg(priv, 14, 0);
+	if (err)
+		return err;
+	msleep(10);
+	err = fc0011_readreg(priv, 14, &vco_cal);
+	if (err)
+		return err;
+	vco_cal &= 0x3F;
+
+	switch (vco_sel) {
+	case 0:
+		if (vco_cal < 8) {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x10 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			err |= fc0011_writereg(priv, 14, 0x80);
+			err |= fc0011_writereg(priv, 14, 0);
+			if (err)
+				return -EIO;
+		} else {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			if (err)
+				return err;
+		}
+		break;
+	case 1:
+		if (vco_cal < 5) {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x8 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			if (err)
+				return err;
+		} else if (vco_cal < 48) {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x10 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			if (err)
+				return err;
+		} else {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			err |= fc0011_writereg(priv, 14, 0x80);
+			err |= fc0011_writereg(priv, 14, 0);
+			if (err)
+				return -EIO;
+		}
+		break;
+	case 2:
+		if (vco_cal > 53) {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x10 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			err |= fc0011_writereg(priv, 14, 0x80);
+			err |= fc0011_writereg(priv, 14, 0);
+			if (err)
+				return -EIO;
+		} else {
+			regs[6] &= 0xE7;
+			regs[6] |= 0x8 | 0x20;
+			err = fc0011_writereg(priv, 6, regs[6]);
+			if (err)
+				return err;
+		}
+		break;
+	}
+	msleep(10);
+
+	err = fc0011_readreg(priv, 13, &regs[13]);
+	if (err)
+		return err;
+	regs[13] |= 0x10;
+	err = fc0011_writereg(priv, 13, regs[13]);
+	if (err)
+		return err;
+	err = fc0011_writereg(priv, 16, 0xB);
+	if (err)
+		return err;
+
+	priv->frequency = freq * 1000;
+	priv->bandwidth = bandwidth * 1000;
+
+	return 0;
+}
+
+static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+        struct fc0011_priv *priv = fe->tuner_priv;
+
+        *frequency = priv->frequency;
+
+        return 0;
+}
+
+static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+        *frequency = 0;
+        return 0;
+}
+
+static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+        struct fc0011_priv *priv = fe->tuner_priv;
+
+        *bandwidth = priv->bandwidth;
+
+        return 0;
+}
+
+static const struct dvb_tuner_ops fc0011_tuner_ops = {
+	.info = {
+		.name		= "Fitipower FC0011",
+
+		.frequency_min	= 170000000, //FIXME?
+		.frequency_max	= 860000000, //FIXME?
+		.frequency_step	= 0,
+        },
+
+	.release		= fc0011_release,
+
+	.init			= fc0011_init,
+	.sleep			= fc0011_sleep,
+
+	.set_params		= fc0011_set_params,
+
+	.get_frequency		= fc0011_get_frequency,
+	.get_if_frequency	= fc0011_get_if_frequency,
+	.get_bandwidth		= fc0011_get_bandwidth,
+};
+
+struct dvb_frontend * fc0011_attach(struct dvb_frontend *fe,
+				    struct i2c_adapter *i2c,
+				    u8 i2c_address)
+{
+        struct fc0011_priv *priv = NULL;
+
+	priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->i2c = i2c;
+	priv->addr = i2c_address;
+
+	dev_info(&priv->i2c->dev, "Fitipower FC0011 attached\n");
+
+	fe->tuner_priv = priv;
+
+	memcpy(&fe->ops.tuner_ops, &fc0011_tuner_ops,
+	       sizeof(struct dvb_tuner_ops));
+
+	return fe;
+}
+EXPORT_SYMBOL(fc0011_attach);
+
+MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
+MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
+MODULE_LICENSE("GPL");
Index: linux-source-3.2/drivers/media/common/tuners/fc0011.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-source-3.2/drivers/media/common/tuners/fc0011.h	2012-03-20 20:34:00.990802668 +0100
@@ -0,0 +1,22 @@
+#ifndef LINUX_FC0011_H_
+#define LINUX_FC0011_H_
+
+#include "dvb_frontend.h"
+
+#if defined(CONFIG_MEDIA_TUNER_FC0011) ||\
+    defined(CONFIG_MEDIA_TUNER_FC0011_MODULE)
+struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
+				   struct i2c_adapter *i2c,
+				   u8 i2c_address);
+#else
+static inline
+struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
+				   struct i2c_adapter *i2c,
+				   u8 i2c_address)
+{
+	dev_err(&i2c->dev, "Driver disabled in Kconfig\n");
+	return NULL;
+}
+#endif
+
+#endif /* LINUX_FC0011_H_ */

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.3: af903x-add-fc0011.patch --]
[-- Type: text/x-patch, Size: 4293 bytes --]

Index: linux-source-3.2/drivers/media/dvb/dvb-usb/Kconfig
===================================================================
--- linux-source-3.2.orig/drivers/media/dvb/dvb-usb/Kconfig	2012-03-20 22:27:53.823367115 +0100
+++ linux-source-3.2/drivers/media/dvb/dvb-usb/Kconfig	2012-03-20 22:29:13.124801711 +0100
@@ -342,6 +342,7 @@
 config DVB_USB_AF903X
 	tristate "Afatech AF903X DVB-T USB2.0 support"
 	depends on DVB_USB && EXPERIMENTAL
+	select MEDIA_TUNER_FC0011   if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_FC0012   if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
 	help
Index: linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-devices.c
===================================================================
--- linux-source-3.2.orig/drivers/media/dvb/dvb-usb/af903x-devices.c	2012-03-20 22:27:53.823367115 +0100
+++ linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-devices.c	2012-03-20 22:29:13.128801781 +0100
@@ -260,6 +260,7 @@
 }
 
 extern struct tuner_desc tuner_af9007;
+extern struct tuner_desc tuner_fc0011;
 extern struct tuner_desc tuner_fc0012;
 extern struct tuner_desc tuner_mxl5007t;
 
@@ -273,6 +274,9 @@
 	case TUNER_AF9007:
 		ctx->tuner_desc = &tuner_af9007;
 		break;
+	case TUNER_FC0011:
+		ctx->tuner_desc = &tuner_fc0011;
+		break;
 	case TUNER_FC0012:
 		ctx->tuner_desc = &tuner_fc0012;
 		break;
Index: linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-fe.c
===================================================================
--- linux-source-3.2.orig/drivers/media/dvb/dvb-usb/af903x-fe.c	2012-03-20 22:27:53.823367115 +0100
+++ linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-fe.c	2012-03-20 22:29:13.132801864 +0100
@@ -28,6 +28,7 @@
 #include "af903x-fe.h"
 #include "af903x-fe-priv.h"
 #include "dvb_frontend.h"
+#include "fc0011.h"
 #include "fc0012.h"
 #include "mxl5007t.h"
 
@@ -1990,6 +1991,12 @@
 	switch(ctx->tuner_desc->tunerId) {
 	case TUNER_AF9007:
 		break;
+	case TUNER_FC0011:
+		ret = dvb_attach(fc0011_attach, frontend, i2c_adap,
+			id == 0 ? ctx->tuner_desc->tuner_addr :
+			ctx->tuner_desc->tuner_addr + 1)
+			== NULL ?  -ENODEV : 0;
+		break;
 	case TUNER_FC0012:
 		ret = dvb_attach(fc0012_attach, frontend, i2c_adap,
 			id == 0 ? ctx->tuner_desc->tuner_addr :
Index: linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-tuners.c
===================================================================
--- linux-source-3.2.orig/drivers/media/dvb/dvb-usb/af903x-tuners.c	2012-03-20 22:27:53.823367115 +0100
+++ linux-source-3.2/drivers/media/dvb/dvb-usb/af903x-tuners.c	2012-03-20 22:29:13.136801932 +0100
@@ -189,6 +189,69 @@
 	{0xf1e6, 0x00},
 };
 
+static u16 fc0011_script_sets[] = {
+	0x38,
+};
+
+static struct af903x_val_set fc0011_scripts[] = {
+	{0x0046, 0x28}, /* TUNER_ID */
+	{0x0057, 0x00},
+	{0x0058, 0x01},
+	{0x005f, 0x00},
+	{0x0060, 0x00},
+	{0x0068, 0xa5},
+	{0x006e, 0x01},
+	{0x0071, 0x0A},
+	{0x0072, 0x02},
+	{0x0074, 0x01},
+	{0x0079, 0x01},
+	{0x0093, 0x00},
+	{0x0094, 0x00},
+	{0x0095, 0x00},
+	{0x0096, 0x00},
+	{0x009b, 0x2D},
+	{0x009c, 0x60},
+	{0x009d, 0x23},
+	{0x00a4, 0x50},
+	{0x00ad, 0x50},
+	{0x00b3, 0x01},
+	{0x00b7, 0x88},
+	{0x00b8, 0xa6},
+	{0x00c3, 0x01},
+	{0x00c4, 0x01},
+	{0x00c7, 0x69},
+	{0xF007, 0x00},
+	{0xF00A, 0x1B},
+	{0xF00B, 0x1B},
+	{0xF00C, 0x1B},
+	{0xF00D, 0x1B},
+	{0xF00E, 0xFF},
+	{0xF00F, 0x01},
+	{0xF010, 0x00},
+	{0xF011, 0x02},
+	{0xF012, 0xFF},
+	{0xF013, 0x01},
+	{0xF014, 0x00},
+	{0xF015, 0x02},
+	{0xF01B, 0xEF},
+	{0xF01C, 0x01},
+	{0xF01D, 0x0f},
+	{0xF01E, 0x02},
+	{0xF01F, 0x6E},
+	{0xF020, 0x00},
+	{0xF025, 0xDE},
+	{0xF026, 0x00},
+	{0xF027, 0x0A},
+	{0xF028, 0x03},
+	{0xF029, 0x6E},
+	{0xF02A, 0x00},
+	{0xF047, 0x00},
+	{0xF054, 0x0},
+	{0xF055, 0x0},
+	{0xF077, 0x01},
+	{0xF1E6, 0x00},
+};
+
 static int af903x_fc0012_init(struct af903x_dev_ctx *ctx, int chip)
 {
 	int ret;
@@ -338,6 +401,19 @@
 	TUNER_FC0012,
 };
 
+struct tuner_desc tuner_fc0011 = {
+	af903x_fc0012_init,
+	af903x_fc0012_sleep,
+	af903x_fc0012_set,
+	fc0011_scripts,
+	fc0011_script_sets,
+	0xc0,
+	1,
+	0,
+	true,
+	TUNER_FC0011,
+};
+
 static u16 mxl5007t_script_sets[] = {
 	0x1e
 };

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

  reply	other threads:[~2012-03-20 21:44 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-22 22:21 [PATCH 2/3] Basic AF9035/AF9033 driver Hans-Frieder Vogt
2012-03-11 15:23 ` [PATCH] AF903X driver update, v1.02 Hans-Frieder Vogt
2012-03-12 17:10   ` Gianluca Gennari
2012-03-12 20:01     ` Malcolm Priestley
2012-03-20  0:28 ` [PATCH 2/3] Basic AF9035/AF9033 driver Mauro Carvalho Chehab
2012-03-20 13:04   ` Michael Büsch
2012-03-20 16:27     ` Gianluca Gennari
2012-03-20 16:37       ` Michael Büsch
2012-03-20 16:49         ` Gianluca Gennari
2012-03-20 17:32           ` Michael Büsch
2012-03-20 21:44             ` Michael Büsch [this message]
2012-03-21  7:52       ` Oliver Schinagl

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120320224438.73ac5d5c@milhouse \
    --to=m@bues.ch \
    --cc=benjamin@southpole.se \
    --cc=gennarone@gmail.com \
    --cc=hfvogt@gmx.net \
    --cc=linux-media@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.