On Sat, 31 Mar 2012 19:34:23 +0300 Antti Palosaari <crope@xxxxxx> wrote: > On 31.03.2012 19:29, Michael Büsch wrote: > > On Sat, 31 Mar 2012 17:28:38 +0300 > > Antti Palosaari<crope@xxxxxx> wrote: > > > >> Googling the filename reveals many links, here is one: > >> http://xgazza.altervista.org/Linux/DVB/dvb-usb-af9035-01.fw > > > > Hm, on tuner register access I get these errors: > > > > [ 9259.080907] af9035_ctrl_msg: command=03 failed fw error=2 > > [ 9259.080922] i2c i2c-8: I2C write reg failed, reg: 07, val: 0f > > > > Is it possible that this firmware is incompatible with my stick? > > The firmware that I successfully used with the other af9035 driver seems to > > be incompatible with your driver, though. It crashes it somewhere > > on firmware download in one of the USB transfer's memcpy. > > Most likely it is incompatible. It is surely one of the earliest > firmwares. I will try to make that new fw downloaded ASAP, likely > tomorrow morning it is done. Ok, thanks a lot. Attached are my current fc0011 patches. Just for reference. -- Greetings, Michael. PGP encryption is encouraged / 908D8B0E
Index: linux/drivers/media/common/tuners/Kconfig =================================================================== --- linux.orig/drivers/media/common/tuners/Kconfig 2012-03-31 00:32:25.000000000 +0200 +++ linux/drivers/media/common/tuners/Kconfig 2012-03-31 13:03:07.279334714 +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-03-31 00:32:25.000000000 +0200 +++ linux/drivers/media/common/tuners/Makefile 2012-03-31 13:03:42.444806267 +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-03-31 14:30:24.122314647 +0200 @@ -0,0 +1,489 @@ +/* + * 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 = ®, .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\n"); + 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, ®s[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-03-31 14:27:31.822958249 +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-03-31 00:32:24.000000000 +0200 +++ linux/MAINTAINERS 2012-03-31 13:03:07.291336671 +0200 @@ -2694,6 +2694,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-03-31 15:25:06.611122203 +0200 +++ linux/drivers/media/dvb/dvb-usb/af9035.c 2012-03-31 18:44:14.557214890 +0200 @@ -634,11 +634,17 @@ enum af9035_id_entry { AF9035_0CCD_0093, + AF9035_15A4_1001, + AF9035_15A4_9035, }; static struct usb_device_id af9035_id[] = { [AF9035_0CCD_0093] = { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)}, + [AF9035_15A4_1001] = { + USB_DEVICE(0x15A4, 0x1001)}, + [AF9035_15A4_9035] = { + USB_DEVICE(0x15A4, 0x9035)}, {}, }; @@ -681,14 +687,20 @@ .i2c_algo = &af9035_i2c_algo, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { .name = "TerraTec Cinergy T Stick", .cold_ids = { &af9035_id[AF9035_0CCD_0093], }, - }, + }, { + .name = "Afatech Technologies DVB-T stick", + .cold_ids = { + &af9035_id[AF9035_15A4_1001], + &af9035_id[AF9035_15A4_9035], + }, + } } }, };
Index: linux/drivers/media/dvb/dvb-usb/af9035.c =================================================================== --- linux.orig/drivers/media/dvb/dvb-usb/af9035.c 2012-03-31 18:44:28.265457286 +0200 +++ linux/drivers/media/dvb/dvb-usb/af9035.c 2012-03-31 18:44:38.241633686 +0200 @@ -19,9 +19,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#define DEBUG 1 + #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); @@ -486,6 +489,7 @@ switch (tmp) { case AF9033_TUNER_TUA9001: + case AF9033_TUNER_FC0011: af9035_af9033_config[i].spec_inv = 1; break; default: @@ -531,6 +535,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; @@ -554,6 +635,7 @@ ret = -ENODEV; goto err; } + adap->fe_adap[0].fe->callback = af9035_frontend_callback; return 0; @@ -567,6 +649,10 @@ .i2c_addr = 0x60, }; +static const struct fc0011_config af9035_fc0011_config = { + .i2c_address = 0xC0, +}; + static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -615,6 +701,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-03-31 18:43:26.392363037 +0200 +++ linux/drivers/media/dvb/frontends/af9033.c 2012-03-31 18:44:38.241633686 +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-03-31 18:43:26.392363037 +0200 +++ linux/drivers/media/dvb/frontends/af9033_priv.h 2012-03-31 18:44:38.241633686 +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