* Add fc0011 tuner support (was: [PATCH 2/3] Basic AF9035/AF9033 driver)
2012-03-20 17:32 ` Michael Büsch
@ 2012-03-20 21:44 ` Michael Büsch
0 siblings, 0 replies; 12+ messages in thread
From: Michael Büsch @ 2012-03-20 21:44 UTC (permalink / raw)
To: Hans-Frieder Vogt; +Cc: gennarone, linux-media, Benjamin Larsson
[-- 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 = ®, .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, ®s[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 = ®, .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, ®s[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 --]
^ permalink raw reply [flat|nested] 12+ messages in thread