Re: [GIT PULL FOR 3.5] AF9035/AF9033/TUA9001 => TerraTec Cinergy T Stick [0ccd:0093]

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

 



On Sun, 01 Apr 2012 15:29:07 +0300
Antti Palosaari <crope@xxxxxx> wrote:
> buf[1] = msg[0].addr << 1;
> Maybe you have given I2C address as a "8bit" format?

Uhh, the address is leftshifted by one.
So I changed the i2c address from 0xC0 to 0x60.

The i2c write seems to work now. At least it doesn't complain anymore
and it sorta seems to tune to the right frequency.
But i2c read may be broken.
I had to enable the commented read code, but it still fails to read
the VCO calibration value:

[ 3101.940765] i2c i2c-8: Failed to read VCO calibration value (got 20)

It doesn't run into this check on the other af903x driver.
So I suspect an i2c read issue here.

Attached: The patches.

-- 
Greetings, Michael.

PGP encryption is encouraged / 908D8B0E
Index: linux/drivers/media/common/tuners/Kconfig
===================================================================
--- linux.orig/drivers/media/common/tuners/Kconfig	2012-04-01 11:42:36.539532984 +0200
+++ linux/drivers/media/common/tuners/Kconfig	2012-04-01 11:42:38.803572117 +0200
@@ -204,6 +204,13 @@
 	help
 	  NXP TDA18218 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_TDA18212
 	tristate "NXP TDA18212 silicon tuner"
 	depends on VIDEO_MEDIA && I2C
Index: linux/drivers/media/common/tuners/Makefile
===================================================================
--- linux.orig/drivers/media/common/tuners/Makefile	2012-04-01 11:42:36.539532984 +0200
+++ linux/drivers/media/common/tuners/Makefile	2012-04-01 11:42:38.803572117 +0200
@@ -29,6 +29,7 @@
 obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
 obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
 obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o
+obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
 
 ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
Index: linux/drivers/media/common/tuners/fc0011.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux/drivers/media/common/tuners/fc0011.c	2012-04-01 14:49:08.046382661 +0200
@@ -0,0 +1,491 @@
+/*
+ * Fitipower FC0011 tuner driver
+ *
+ * Copyright (C) 2012 Michael Buesch <m@xxxxxxx>
+ *
+ * Derived from FC0012 tuner driver:
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@xxxxxxx>
+ *
+ * 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"
+
+
+/* Tuner registers */
+enum {
+	FC11_REG_0,
+	FC11_REG_FA,		/* FA */
+	FC11_REG_FP,		/* FP */
+	FC11_REG_XINHI,		/* XIN high 8 bit */
+	FC11_REG_XINLO,		/* XIN low 8 bit */
+	FC11_REG_VCO,		/* VCO */
+	FC11_REG_VCOSEL,	/* VCO select */
+	FC11_REG_7,		/* Unknown tune reg 7 */
+	FC11_REG_8,		/* Unknown tune reg 8 */
+	FC11_REG_9,
+	FC11_REG_10,		/* Unknown tune reg 10 */
+	FC11_REG_11,		/* Unknown tune reg 11 */
+	FC11_REG_12,
+	FC11_REG_RCCAL,		/* RC calibrate */
+	FC11_REG_VCOCAL,	/* VCO calibrate */
+	FC11_REG_15,
+	FC11_REG_16,		/* Unknown tune reg 16 */
+	FC11_REG_17,
+
+	FC11_NR_REGS,		/* Number of registers */
+};
+
+enum FC11_REG_VCOSEL_bits {
+	FC11_VCOSEL_2		= 0x08, /* VCO select 2 */
+	FC11_VCOSEL_1		= 0x10, /* VCO select 1 */
+	FC11_VCOSEL_CLKOUT	= 0x20, /* Fix clock out */
+	FC11_VCOSEL_BW7M	= 0x40, /* 7MHz bw */
+	FC11_VCOSEL_BW6M	= 0x80, /* 6MHz bw */
+};
+
+enum FC11_REG_RCCAL_bits {
+	FC11_RCCAL_FORCE	= 0x10, /* force */
+};
+
+enum FC11_REG_VCOCAL_bits {
+	FC11_VCOCAL_RUN		= 0,	/* VCO calibration run */
+	FC11_VCOCAL_VALUEMASK	= 0x3F,	/* VCO calibration value mask */
+	FC11_VCOCAL_OK		= 0x40,	/* VCO calibration Ok */
+	FC11_VCOCAL_RESET	= 0x80, /* VCO calibration reset */
+};
+
+
+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)
+{
+	struct fc0011_priv *priv = fe->tuner_priv;
+	int err;
+
+	if (WARN_ON(!fe->callback))
+		return -EINVAL;
+	err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+			   FC0011_FE_CALLBACK_POWER, 0);
+	if (err) {
+		dev_err(&priv->i2c->dev, "power-on callback failed\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static int fc0011_set_params(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct fc0011_priv *priv = fe->tuner_priv;
+	int err;
+	unsigned int i;
+	u32 freq = p->frequency / 1000;
+	u32 bandwidth = p->bandwidth_hz / 1000;
+	u32 fvco, xin, xdiv, xdivr;
+	u16 frac;
+	u8 fa, fp, vco_sel, vco_cal;
+	u8 regs[FC11_NR_REGS] = { };
+
+	regs[FC11_REG_7] = 0x0F;
+	regs[FC11_REG_8] = 0x3E;
+	regs[FC11_REG_10] = 0xB8;
+	regs[FC11_REG_11] = 0x80;
+	regs[FC11_REG_RCCAL] = 0x04;
+	err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
+	err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
+	err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
+	err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
+	err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
+	if (err)
+		return -EIO;
+
+	/* Set VCO freq and VCO div */
+	if (freq < 54000) {
+		fvco = freq * 64;
+		regs[FC11_REG_VCO] = 0x82;
+	} else if (freq < 108000) {
+		fvco = freq * 32;
+		regs[FC11_REG_VCO] = 0x42;
+	} else if (freq < 216000) {
+		fvco = freq * 16;
+		regs[FC11_REG_VCO] = 0x22;
+	} else if (freq < 432000) {
+		fvco = freq * 8;
+		regs[FC11_REG_VCO] = 0x12;
+	} else {
+		fvco = freq * 4;
+		regs[FC11_REG_VCO] = 0x0A;
+	}
+
+	/* Calc XIN. The PLL reference frequency is 18 MHz. */
+	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 = 512;
+	else if (frac < 65026)
+		xin = frac;
+	else
+		xin = 65024;
+	regs[FC11_REG_XINHI] = xin >> 8;
+	regs[FC11_REG_XINLO] = xin;
+
+	/* Calc FP and FA */
+	xdivr = xdiv;
+	if (fvco - xdiv * 18000 >= 9000)
+		xdivr += 1; /* round */
+	fp = xdivr / 8;
+	fa = xdivr - fp * 8;
+	if (fa < 2) {
+		fp -= 1;
+		fa += 8;
+	}
+	if (fp > 0x1F) {
+		fp &= 0x1F;
+		fa &= 0xF;
+	}
+	if (fa >= fp) {
+		dev_warn(&priv->i2c->dev,
+			 "fa %02X >= fp %02X, but trying to continue\n",
+			 (unsigned int)(u8)fa, (unsigned int)(u8)fp);
+	}
+	regs[FC11_REG_FA] = fa;
+	regs[FC11_REG_FP] = fp;
+
+	/* Select bandwidth */
+	switch (bandwidth) {
+	case 8000:
+		break;
+	case 7000:
+		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M;
+		break;
+	default:
+	case 6000:
+		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M;
+		break;
+	}
+
+	/* Pre VCO select */
+	if (fvco < 2320000) {
+		vco_sel = 0;
+		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+	} else if (fvco < 3080000) {
+		vco_sel = 1;
+		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
+	} else {
+		vco_sel = 2;
+		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
+	}
+
+	/* Fix for low freqs */
+	if (freq < 45000) {
+		regs[FC11_REG_FA] = 0x6;
+		regs[FC11_REG_FP] = 0x11;
+	}
+
+	/* Clock out fix */
+	regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT;
+
+	/* Write the cached registers */
+	for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) {
+		err = fc0011_writereg(priv, i, regs[i]);
+		if (err)
+			return err;
+	}
+
+	/* VCO calibration */
+	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET);
+	if (err)
+		return err;
+	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
+	if (err)
+		return err;
+	/* Read calib val */
+	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
+	if (err)
+		return err;
+	msleep(10);
+	err = fc0011_readreg(priv, FC11_REG_VCOCAL, &vco_cal);
+	if (err)
+		return err;
+	if (!(vco_cal & FC11_VCOCAL_OK)) {
+		/* Reset the tuner and try again */
+		err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+				   FC0011_FE_CALLBACK_RESET, 0);
+		if (err) {
+			dev_err(&priv->i2c->dev, "Failed to reset tuner\n");
+			return err;
+		}
+		/* Reinit tuner config */
+		err = 0;
+		for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++)
+			err |= fc0011_writereg(priv, i, regs[i]);
+		err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
+		err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
+		err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
+		err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
+		err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
+		if (err)
+			return -EIO;
+		/* VCO calibration */
+		err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET);
+		if (err)
+			return err;
+		err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
+		if (err)
+			return err;
+		/* Read calib val */
+		err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
+		if (err)
+			return err;
+		msleep(10);
+		err = fc0011_readreg(priv, FC11_REG_VCOCAL, &vco_cal);
+		if (err)
+			return err;
+	}
+	if (!(vco_cal & FC11_VCOCAL_OK)) {
+		dev_err(&priv->i2c->dev,
+			"Failed to read VCO calibration value (got %02X)\n",
+			(unsigned int)(u8)vco_cal);
+		return -EIO;
+	}
+	vco_cal &= FC11_VCOCAL_VALUEMASK;
+
+	switch (vco_sel) {
+	case 0:
+		if (vco_cal < 8) {
+			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
+			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+					      regs[FC11_REG_VCOSEL]);
+			err |= fc0011_writereg(priv, FC11_REG_VCOCAL,
+					       FC11_VCOCAL_RESET);
+			err |= fc0011_writereg(priv, FC11_REG_VCOCAL,
+					       FC11_VCOCAL_RUN);
+			if (err)
+				return -EIO;
+		} else {
+			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+					      regs[FC11_REG_VCOSEL]);
+			if (err)
+				return err;
+		}
+		break;
+	case 1:
+		if (vco_cal < 5) {
+			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
+			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+					      regs[FC11_REG_VCOSEL]);
+			err |= fc0011_writereg(priv, FC11_REG_VCOCAL,
+					       FC11_VCOCAL_RESET);
+			err |= fc0011_writereg(priv, FC11_REG_VCOCAL,
+					       FC11_VCOCAL_RUN);
+			if (err)
+				return -EIO;
+		} else if (vco_cal <= 48) {
+			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
+			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+					      regs[FC11_REG_VCOSEL]);
+			if (err)
+				return err;
+		} else {
+			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+					      regs[FC11_REG_VCOSEL]);
+			err |= fc0011_writereg(priv, FC11_REG_VCOCAL,
+					       FC11_VCOCAL_RESET);
+			err |= fc0011_writereg(priv, FC11_REG_VCOCAL,
+					       FC11_VCOCAL_RUN);
+			if (err)
+				return -EIO;
+		}
+		break;
+	case 2:
+		if (vco_cal > 53) {
+			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
+			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+					      regs[FC11_REG_VCOSEL]);
+			err |= fc0011_writereg(priv, FC11_REG_VCOCAL,
+					       FC11_VCOCAL_RESET);
+			err |= fc0011_writereg(priv, FC11_REG_VCOCAL,
+					       FC11_VCOCAL_RUN);
+			if (err)
+				return -EIO;
+		} else {
+			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
+			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+					      regs[FC11_REG_VCOSEL]);
+			if (err)
+				return err;
+		}
+		break;
+	}
+	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
+	if (err)
+		return err;
+	msleep(10);
+	err = fc0011_readreg(priv, FC11_REG_VCOCAL, &vco_cal);
+	if (err)
+		return err;
+	msleep(10);
+
+	err = fc0011_readreg(priv, FC11_REG_RCCAL, &regs[FC11_REG_RCCAL]);
+	if (err)
+		return err;
+	regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE;
+	err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
+	if (err)
+		return err;
+	err = fc0011_writereg(priv, FC11_REG_16, 0xB);
+	if (err)
+		return err;
+
+	priv->frequency = p->frequency;
+	priv->bandwidth = p->bandwidth_hz;
+
+	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	= 45000000,
+		.frequency_max	= 1000000000,
+	},
+
+	.release		= fc0011_release,
+	.init			= fc0011_init,
+
+	.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,
+				   const struct fc0011_config *config)
+{
+	struct fc0011_priv *priv;
+
+	priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->i2c = i2c;
+	priv->addr = config->i2c_address;
+
+	fe->tuner_priv = priv;
+	fe->ops.tuner_ops = fc0011_tuner_ops;
+
+	dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n");
+
+	return fe;
+}
+EXPORT_SYMBOL(fc0011_attach);
+
+MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
+MODULE_AUTHOR("Michael Buesch <m@xxxxxxx>");
+MODULE_LICENSE("GPL");
Index: linux/drivers/media/common/tuners/fc0011.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux/drivers/media/common/tuners/fc0011.h	2012-04-01 11:42:38.807572185 +0200
@@ -0,0 +1,41 @@
+#ifndef LINUX_FC0011_H_
+#define LINUX_FC0011_H_
+
+#include "dvb_frontend.h"
+
+
+/** struct fc0011_config - fc0011 hardware config
+ *
+ * @i2c_address: I2C bus address.
+ */
+struct fc0011_config {
+	u8 i2c_address;
+};
+
+/** enum fc0011_fe_callback_commands - Frontend callbacks
+ *
+ * @FC0011_FE_CALLBACK_POWER: Power on tuner hardware.
+ * @FC0011_FE_CALLBACK_RESET: Request a tuner reset.
+ */
+enum fc0011_fe_callback_commands {
+	FC0011_FE_CALLBACK_POWER,
+	FC0011_FE_CALLBACK_RESET,
+};
+
+#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,
+				   const struct fc0011_config *config);
+#else
+static inline
+struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
+				   struct i2c_adapter *i2c,
+				   const struct fc0011_config *config)
+{
+	dev_err(&i2c->dev, "fc0011 driver disabled in Kconfig\n");
+	return NULL;
+}
+#endif
+
+#endif /* LINUX_FC0011_H_ */
Index: linux/MAINTAINERS
===================================================================
--- linux.orig/MAINTAINERS	2012-04-01 11:42:36.539532984 +0200
+++ linux/MAINTAINERS	2012-04-01 11:42:38.819572398 +0200
@@ -2697,6 +2697,13 @@
 F:	Documentation/hwmon/f71805f
 F:	drivers/hwmon/f71805f.c
 
+FC0011 TUNER DRIVER
+M:	Michael Buesch <m@xxxxxxx>
+L:	linux-media@xxxxxxxxxxxxxxx
+S:	Maintained
+F:	drivers/media/common/tuners/fc0011.h
+F:	drivers/media/common/tuners/fc0011.c
+
 FANOTIFY
 M:	Eric Paris <eparis@xxxxxxxxxx>
 S:	Maintained
Index: linux/drivers/media/dvb/dvb-usb/af9035.c
===================================================================
--- linux.orig/drivers/media/dvb/dvb-usb/af9035.c	2012-04-01 11:42:38.867573221 +0200
+++ linux/drivers/media/dvb/dvb-usb/af9035.c	2012-04-01 14:44:20.848955668 +0200
@@ -22,6 +22,7 @@
 #include "af9035.h"
 #include "af9033.h"
 #include "tua9001.h"
+#include "fc0011.h"
 
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 static DEFINE_MUTEX(af9035_usb_mutex);
@@ -209,12 +210,6 @@
 					msg[1].len);
 		} else {
 			/* I2C */
-#if 0
-			/*
-			 * FIXME: Keep that code. It should work but as it is
-			 * not tested I left it disabled and return -EOPNOTSUPP
-			 * for the sure.
-			 */
 			u8 buf[4 + msg[0].len];
 			struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf),
 					buf, msg[1].len, msg[1].buf };
@@ -224,9 +219,6 @@
 			buf[3] = 0x00;
 			memcpy(&buf[4], msg[0].buf, msg[0].len);
 			ret = af9035_ctrl_msg(d->udev, &req);
-#endif
-			pr_debug("%s: I2C operation not supported\n", __func__);
-			ret = -EOPNOTSUPP;
 		}
 	} else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
 		if (msg[0].len > 40) {
@@ -480,6 +472,7 @@
 
 		switch (tmp) {
 		case AF9033_TUNER_TUA9001:
+		case AF9033_TUNER_FC0011:
 			af9035_af9033_config[i].spec_inv = 1;
 			break;
 		default:
@@ -524,6 +517,83 @@
 	return ret;
 }
 
+static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d,
+					int cmd, int arg)
+{
+	int err;
+
+	switch (cmd) {
+	case FC0011_FE_CALLBACK_POWER:
+		/* Tuner enable */
+		err = af9035_wr_reg_mask(d, 0xd8eb, 1, 1);
+		if (err)
+			return err;
+		err = af9035_wr_reg_mask(d, 0xd8ec, 1, 1);
+		if (err)
+			return err;
+		err = af9035_wr_reg_mask(d, 0xd8ed, 1, 1);
+		if (err)
+			return err;
+		/* LED */
+		err = af9035_wr_reg_mask(d, 0xd8d0, 1, 1);
+		if (err)
+			return err;
+		err = af9035_wr_reg_mask(d, 0xd8d1, 1, 1);
+		if (err)
+			return err;
+		msleep(10);
+		break;
+	case FC0011_FE_CALLBACK_RESET:
+		err = af9035_wr_reg(d, 0xd8e9, 1);
+		if (err)
+			return err;
+		err = af9035_wr_reg(d, 0xd8e8, 1);
+		if (err)
+			return err;
+		err = af9035_wr_reg(d, 0xd8e7, 1);
+		if (err)
+			return err;
+		msleep(10);
+		err = af9035_wr_reg(d, 0xd8e7, 0);
+		if (err)
+			return err;
+		msleep(10);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg)
+{
+	switch (af9035_af9033_config[0].tuner) {
+	case AF9033_TUNER_FC0011:
+		return af9035_fc0011_tuner_callback(d, cmd, arg);
+	default:
+		break;
+	}
+
+	return -ENODEV;
+}
+
+static int af9035_frontend_callback(void *adapter_priv, int component,
+				    int cmd, int arg)
+{
+	struct i2c_adapter *adap = adapter_priv;
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+
+	switch (component) {
+	case DVB_FRONTEND_COMPONENT_TUNER:
+		return af9035_tuner_callback(d, cmd, arg);
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
 static int af9035_frontend_attach(struct dvb_usb_adapter *adap)
 {
 	int ret;
@@ -552,6 +622,7 @@
 		ret = -ENODEV;
 		goto err;
 	}
+	adap->fe_adap[0].fe->callback = af9035_frontend_callback;
 
 	return 0;
 
@@ -565,6 +636,10 @@
 	.i2c_addr = 0x60,
 };
 
+static const struct fc0011_config af9035_fc0011_config = {
+	.i2c_address = 0x60,
+};
+
 static int af9035_tuner_attach(struct dvb_usb_adapter *adap)
 {
 	int ret;
@@ -613,6 +688,10 @@
 		fe = dvb_attach(tua9001_attach, adap->fe_adap[0].fe,
 				&adap->dev->i2c_adap, &af9035_tua9001_config);
 		break;
+	case AF9033_TUNER_FC0011:
+		fe = dvb_attach(fc0011_attach, adap->fe_adap[0].fe,
+				&adap->dev->i2c_adap, &af9035_fc0011_config);
+		break;
 	default:
 		fe = NULL;
 	}
Index: linux/drivers/media/dvb/frontends/af9033.c
===================================================================
--- linux.orig/drivers/media/dvb/frontends/af9033.c	2012-04-01 11:42:36.147526208 +0200
+++ linux/drivers/media/dvb/frontends/af9033.c	2012-04-01 11:42:38.919574120 +0200
@@ -297,6 +297,10 @@
 		len = ARRAY_SIZE(tuner_init_tua9001);
 		init = tuner_init_tua9001;
 		break;
+	case AF9033_TUNER_FC0011:
+		len = ARRAY_SIZE(tuner_init_fc0011);
+		init = tuner_init_fc0011;
+		break;
 	default:
 		pr_debug("%s: unsupported tuner ID=%d\n", __func__,
 				state->cfg.tuner);
Index: linux/drivers/media/dvb/frontends/af9033_priv.h
===================================================================
--- linux.orig/drivers/media/dvb/frontends/af9033_priv.h	2012-04-01 11:42:36.147526208 +0200
+++ linux/drivers/media/dvb/frontends/af9033_priv.h	2012-04-01 11:42:38.919574120 +0200
@@ -238,5 +238,66 @@
 	{ 0x80f1e6, 0x00 },
 };
 
+/* Fitipower fc0011 tuner init
+   AF9033_TUNER_FC0011    = 0x28 */
+static const struct reg_val tuner_init_fc0011[] = {
+	{ 0x800046, AF9033_TUNER_FC0011 },
+	{ 0x800057, 0x00 },
+	{ 0x800058, 0x01 },
+	{ 0x80005f, 0x00 },
+	{ 0x800060, 0x00 },
+	{ 0x800068, 0xa5 },
+	{ 0x80006e, 0x01 },
+	{ 0x800071, 0x0A },
+	{ 0x800072, 0x02 },
+	{ 0x800074, 0x01 },
+	{ 0x800079, 0x01 },
+	{ 0x800093, 0x00 },
+	{ 0x800094, 0x00 },
+	{ 0x800095, 0x00 },
+	{ 0x800096, 0x00 },
+	{ 0x80009b, 0x2D },
+	{ 0x80009c, 0x60 },
+	{ 0x80009d, 0x23 },
+	{ 0x8000a4, 0x50 },
+	{ 0x8000ad, 0x50 },
+	{ 0x8000b3, 0x01 },
+	{ 0x8000b7, 0x88 },
+	{ 0x8000b8, 0xa6 },
+	{ 0x8000c3, 0x01 },
+	{ 0x8000c4, 0x01 },
+	{ 0x8000c7, 0x69 },
+	{ 0x80F007, 0x00 },
+	{ 0x80F00A, 0x1B },
+	{ 0x80F00B, 0x1B },
+	{ 0x80F00C, 0x1B },
+	{ 0x80F00D, 0x1B },
+	{ 0x80F00E, 0xFF },
+	{ 0x80F00F, 0x01 },
+	{ 0x80F010, 0x00 },
+	{ 0x80F011, 0x02 },
+	{ 0x80F012, 0xFF },
+	{ 0x80F013, 0x01 },
+	{ 0x80F014, 0x00 },
+	{ 0x80F015, 0x02 },
+	{ 0x80F01B, 0xEF },
+	{ 0x80F01C, 0x01 },
+	{ 0x80F01D, 0x0f },
+	{ 0x80F01E, 0x02 },
+	{ 0x80F01F, 0x6E },
+	{ 0x80F020, 0x00 },
+	{ 0x80F025, 0xDE },
+	{ 0x80F026, 0x00 },
+	{ 0x80F027, 0x0A },
+	{ 0x80F028, 0x03 },
+	{ 0x80F029, 0x6E },
+	{ 0x80F02A, 0x00 },
+	{ 0x80F047, 0x00 },
+	{ 0x80F054, 0x00 },
+	{ 0x80F055, 0x00 },
+	{ 0x80F077, 0x01 },
+	{ 0x80F1E6, 0x00 },
+};
+
 #endif /* AF9033_PRIV_H */
 

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux