diff options
Diffstat (limited to 'drivers')
62 files changed, 4552 insertions, 523 deletions
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 4a6d5cef3964..0fd15d925e15 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -204,6 +204,13 @@ config MEDIA_TUNER_TDA18218 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 @@ -211,4 +218,10 @@ config MEDIA_TUNER_TDA18212 help NXP TDA18212 silicon tuner driver. +config MEDIA_TUNER_TUA9001 + tristate "Infineon TUA 9001 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Infineon TUA 9001 silicon tuner driver. endmenu diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index f80407eb8998..64ee06fa83f1 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -28,6 +28,8 @@ obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o 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_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 diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/common/tuners/fc0011.c new file mode 100644 index 000000000000..e4882546c283 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.c @@ -0,0 +1,524 @@ +/* + * 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" + + +/* 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 tuner reg 7 */ + FC11_REG_8, /* Unknown tuner reg 8 */ + FC11_REG_9, + FC11_REG_10, /* Unknown tuner reg 10 */ + FC11_REG_11, /* Unknown tuner reg 11 */ + FC11_REG_12, + FC11_REG_RCCAL, /* RC calibrate */ + FC11_REG_VCOCAL, /* VCO calibrate */ + FC11_REG_15, + FC11_REG_16, /* Unknown tuner 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 }; + + 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) +{ + u8 dummy; + struct i2c_msg msg[2] = { + { .addr = priv->addr, + .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, + .flags = I2C_M_RD, .buf = val ? : &dummy, .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, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Power-on callback failed\n"); + return err; + } + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Reset callback failed\n"); + return err; + } + + return 0; +} + +/* Initiate VCO calibration */ +static int fc0011_vcocal_trigger(struct fc0011_priv *priv) +{ + int err; + + 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; + + return 0; +} + +/* Read VCO calibration value */ +static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) +{ + int err; + + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); + if (err) + return err; + usleep_range(10000, 20000); + err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); + if (err) + 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, vco_retries; + 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: + dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. " + "Using 6000 kHz.\n", + bandwidth); + bandwidth = 6000; + /* fallthrough */ + 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_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries = 0; + while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) { + /* Reset the tuner and try again */ + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + 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_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries++; + } + if (!(vco_cal & FC11_VCOCAL_OK)) { + dev_err(&priv->i2c->dev, + "Failed to read VCO calibration value (got %02X)\n", + (unsigned int)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]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + 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]); + 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]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } 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]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } + 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]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } 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_vcocal_read(priv, NULL); + if (err) + return err; + usleep_range(10000, 50000); + + 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; + + dev_dbg(&priv->i2c->dev, "Tuned to " + "fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X " + "vcocal=%02X(%u) bw=%u\n", + (unsigned int)regs[FC11_REG_FA], + (unsigned int)regs[FC11_REG_FP], + (unsigned int)regs[FC11_REG_XINHI], + (unsigned int)regs[FC11_REG_XINLO], + (unsigned int)regs[FC11_REG_VCO], + (unsigned int)regs[FC11_REG_VCOSEL], + (unsigned int)vco_cal, vco_retries, + (unsigned int)bandwidth); + + 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@bues.ch>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/fc0011.h b/drivers/media/common/tuners/fc0011.h new file mode 100644 index 000000000000..0ee581f122d2 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.h @@ -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_ */ diff --git a/drivers/media/common/tuners/tua9001.c b/drivers/media/common/tuners/tua9001.c new file mode 100644 index 000000000000..de2607084672 --- /dev/null +++ b/drivers/media/common/tuners/tua9001.c @@ -0,0 +1,215 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "tua9001.h" +#include "tua9001_priv.h" + +/* write register */ +static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val) +{ + int ret; + u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff }; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + printk(KERN_WARNING "%s: I2C wr failed=%d reg=%02x\n", + __func__, ret, reg); + ret = -EREMOTEIO; + } + + return ret; +} + +static int tua9001_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int tua9001_init(struct dvb_frontend *fe) +{ + struct tua9001_priv *priv = fe->tuner_priv; + int ret = 0; + u8 i; + struct reg_val data[] = { + { 0x1e, 0x6512 }, + { 0x25, 0xb888 }, + { 0x39, 0x5460 }, + { 0x3b, 0x00c0 }, + { 0x3a, 0xf000 }, + { 0x08, 0x0000 }, + { 0x32, 0x0030 }, + { 0x41, 0x703a }, + { 0x40, 0x1c78 }, + { 0x2c, 0x1c00 }, + { 0x36, 0xc013 }, + { 0x37, 0x6f18 }, + { 0x27, 0x0008 }, + { 0x2a, 0x0001 }, + { 0x34, 0x0a40 }, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + + if (ret < 0) + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int tua9001_set_params(struct dvb_frontend *fe) +{ + struct tua9001_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u16 val; + u32 frequency; + struct reg_val data[2]; + + pr_debug("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", + __func__, c->delivery_system, c->frequency, + c->bandwidth_hz); + + switch (c->delivery_system) { + case SYS_DVBT: + switch (c->bandwidth_hz) { + case 8000000: + val = 0x0000; + break; + case 7000000: + val = 0x1000; + break; + case 6000000: + val = 0x2000; + break; + case 5000000: + val = 0x3000; + break; + default: + ret = -EINVAL; + goto err; + } + break; + default: + ret = -EINVAL; + goto err; + } + + data[0].reg = 0x04; + data[0].val = val; + + frequency = (c->frequency - 150000000); + frequency /= 100; + frequency *= 48; + frequency /= 10000; + + data[1].reg = 0x1f; + data[1].val = frequency; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); + if (ret < 0) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + +err: + if (ret < 0) + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; /* Zero-IF */ + + return 0; +} + +static const struct dvb_tuner_ops tua9001_tuner_ops = { + .info = { + .name = "Infineon TUA 9001", + + .frequency_min = 170000000, + .frequency_max = 862000000, + .frequency_step = 0, + }, + + .release = tua9001_release, + + .init = tua9001_init, + .set_params = tua9001_set_params, + + .get_if_frequency = tua9001_get_if_frequency, +}; + +struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg) +{ + struct tua9001_priv *priv = NULL; + + priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + + printk(KERN_INFO "Infineon TUA 9001 successfully attached."); + + memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL(tua9001_attach); + +MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/tua9001.h b/drivers/media/common/tuners/tua9001.h new file mode 100644 index 000000000000..38d6ae76b1d6 --- /dev/null +++ b/drivers/media/common/tuners/tua9001.h @@ -0,0 +1,46 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TUA9001_H +#define TUA9001_H + +#include "dvb_frontend.h" + +struct tua9001_config { + /* + * I2C address + */ + u8 i2c_addr; +}; + +#if defined(CONFIG_MEDIA_TUNER_TUA9001) || \ + (defined(CONFIG_MEDIA_TUNER_TUA9001_MODULE) && defined(MODULE)) +extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg); +#else +static inline struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/tua9001_priv.h b/drivers/media/common/tuners/tua9001_priv.h new file mode 100644 index 000000000000..73cc1ce0575c --- /dev/null +++ b/drivers/media/common/tuners/tua9001_priv.h @@ -0,0 +1,34 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TUA9001_PRIV_H +#define TUA9001_PRIV_H + +struct reg_val { + u8 reg; + u16 val; +}; + +struct tua9001_priv { + struct tua9001_config *cfg; + struct i2c_adapter *i2c; +}; + +#endif diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 63bf45679f98..be1db75091ff 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -422,3 +422,15 @@ config DVB_USB_RTL28XXU select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Realtek RTL28xxU DVB USB receiver. + +config DVB_USB_AF9035 + tristate "Afatech AF9035 DVB-T USB2.0 support" + depends on DVB_USB + select DVB_AF9033 + select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Afatech AF9035 based DVB USB receiver. + diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index b76acb5387e6..b667ac39a4e3 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -110,6 +110,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o dvb-usb-rtl28xxu-objs = rtl28xxu.o obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o +dvb-usb-af9035-objs = af9035.o +obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o + ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends/ # due to tuner-xc3028 diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c new file mode 100644 index 000000000000..e1d6e6efa77b --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -0,0 +1,1164 @@ +/* + * Afatech AF9035 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "af9035.h" +#include "af9033.h" +#include "tua9001.h" +#include "fc0011.h" +#include "mxl5007t.h" +#include "tda18218.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +static DEFINE_MUTEX(af9035_usb_mutex); +static struct config af9035_config; +static struct dvb_usb_device_properties af9035_properties[2]; +static int af9035_properties_count = ARRAY_SIZE(af9035_properties); +static struct af9033_config af9035_af9033_config[] = { + { + .ts_mode = AF9033_TS_MODE_USB, + }, { + .ts_mode = AF9033_TS_MODE_SERIAL, + } +}; + +static u16 af9035_checksum(const u8 *buf, size_t len) +{ + size_t i; + u16 checksum = 0; + + for (i = 1; i < len; i++) { + if (i % 2) + checksum += buf[i] << 8; + else + checksum += buf[i]; + } + checksum = ~checksum; + + return checksum; +} + +static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) +{ +#define BUF_LEN 63 +#define REQ_HDR_LEN 4 /* send header size */ +#define ACK_HDR_LEN 3 /* rece header size */ +#define CHECKSUM_LEN 2 +#define USB_TIMEOUT 2000 + + int ret, act_len; + u8 buf[BUF_LEN]; + u32 msg_len; + static u8 seq; /* packet sequence number */ + u16 checksum, tmpsum; + + /* buffer overflow check */ + if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || + req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) { + pr_debug("%s: too much data wlen=%d rlen=%d\n", __func__, + req->wlen, req->rlen); + return -EINVAL; + } + + if (mutex_lock_interruptible(&af9035_usb_mutex) < 0) + return -EAGAIN; + + buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1; + buf[1] = req->mbox; + buf[2] = req->cmd; + buf[3] = seq++; + if (req->wlen) + memcpy(&buf[4], req->wbuf, req->wlen); + + /* calc and add checksum */ + checksum = af9035_checksum(buf, buf[0] - 1); + buf[buf[0]-1] = (checksum >> 8); + buf[buf[0]-0] = (checksum & 0xff); + + msg_len = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN ; + + /* send req */ + ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len, + &act_len, USB_TIMEOUT); + if (ret < 0) + err("bulk message failed=%d (%d/%d)", ret, msg_len, act_len); + else + if (act_len != msg_len) + ret = -EIO; /* all data is not send */ + if (ret < 0) + goto err_mutex_unlock; + + /* no ack for those packets */ + if (req->cmd == CMD_FW_DL) + goto exit_mutex_unlock; + + /* receive ack and data if read req */ + msg_len = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN; + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, + &act_len, USB_TIMEOUT); + if (ret < 0) { + err("recv bulk message failed=%d", ret); + ret = -EIO; + goto err_mutex_unlock; + } + if (act_len != msg_len) { + err("recv bulk message truncated (%d != %u)\n", + act_len, (unsigned int)msg_len); + ret = -EIO; + goto err_mutex_unlock; + } + + /* verify checksum */ + checksum = af9035_checksum(buf, act_len - 2); + tmpsum = (buf[act_len - 2] << 8) | buf[act_len - 1]; + if (tmpsum != checksum) { + err("%s: command=%02X checksum mismatch (%04X != %04X)\n", + __func__, req->cmd, + (unsigned int)tmpsum, (unsigned int)checksum); + ret = -EIO; + goto err_mutex_unlock; + } + /* check status */ + if (buf[2]) { + pr_debug("%s: command=%02x failed fw error=%d\n", __func__, + req->cmd, buf[2]); + ret = -EIO; + goto err_mutex_unlock; + } + + /* read request, copy returned data to return buf */ + if (req->rlen) + memcpy(req->rbuf, &buf[ACK_HDR_LEN], req->rlen); + +err_mutex_unlock: +exit_mutex_unlock: + mutex_unlock(&af9035_usb_mutex); + + return ret; +} + +/* write multiple registers */ +static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) +{ + u8 wbuf[6 + len]; + u8 mbox = (reg >> 16) & 0xff; + struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL }; + + wbuf[0] = len; + wbuf[1] = 2; + wbuf[2] = 0; + wbuf[3] = 0; + wbuf[4] = (reg >> 8) & 0xff; + wbuf[5] = (reg >> 0) & 0xff; + memcpy(&wbuf[6], val, len); + + return af9035_ctrl_msg(d->udev, &req); +} + +/* read multiple registers */ +static int af9035_rd_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) +{ + u8 wbuf[] = { len, 2, 0, 0, (reg >> 8) & 0xff, reg & 0xff }; + u8 mbox = (reg >> 16) & 0xff; + struct usb_req req = { CMD_MEM_RD, mbox, sizeof(wbuf), wbuf, len, val }; + + return af9035_ctrl_msg(d->udev, &req); +} + +/* write single register */ +static int af9035_wr_reg(struct dvb_usb_device *d, u32 reg, u8 val) +{ + return af9035_wr_regs(d, reg, &val, 1); +} + +/* read single register */ +static int af9035_rd_reg(struct dvb_usb_device *d, u32 reg, u8 *val) +{ + return af9035_rd_regs(d, reg, val, 1); +} + +/* write single register with mask */ +static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val, + u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = af9035_rd_regs(d, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return af9035_wr_regs(d, reg, &val, 1); +} + +static int af9035_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + /* + * I2C sub header is 5 bytes long. Meaning of those bytes are: + * 0: data len + * 1: I2C addr << 1 + * 2: reg addr len + * byte 3 and 4 can be used as reg addr + * 3: reg addr MSB + * used when reg addr len is set to 2 + * 4: reg addr LSB + * used when reg addr len is set to 1 or 2 + * + * For the simplify we do not use register addr at all. + * NOTE: As a firmware knows tuner type there is very small possibility + * there could be some tuner I2C hacks done by firmware and this may + * lead problems if firmware expects those bytes are used. + */ + if (num == 2 && !(msg[0].flags & I2C_M_RD) && + (msg[1].flags & I2C_M_RD)) { + if (msg[0].len > 40 || msg[1].len > 40) { + /* TODO: correct limits > 40 */ + ret = -EOPNOTSUPP; + } else if (msg[0].addr == af9035_af9033_config[0].i2c_addr) { + /* integrated demod */ + u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; + ret = af9035_rd_regs(d, reg, &msg[1].buf[0], + msg[1].len); + } else { + /* I2C */ + u8 buf[5 + msg[0].len]; + struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), + buf, msg[1].len, msg[1].buf }; + buf[0] = msg[1].len; + buf[1] = msg[0].addr << 1; + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + ret = af9035_ctrl_msg(d->udev, &req); + } + } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + if (msg[0].len > 40) { + /* TODO: correct limits > 40 */ + ret = -EOPNOTSUPP; + } else if (msg[0].addr == af9035_af9033_config[0].i2c_addr) { + /* integrated demod */ + u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; + ret = af9035_wr_regs(d, reg, &msg[0].buf[3], + msg[0].len - 3); + } else { + /* I2C */ + u8 buf[5 + msg[0].len]; + struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf, + 0, NULL }; + buf[0] = msg[0].len; + buf[1] = msg[0].addr << 1; + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + ret = af9035_ctrl_msg(d->udev, &req); + } + } else { + /* + * We support only two kind of I2C transactions: + * 1) 1 x read + 1 x write + * 2) 1 x write + */ + ret = -EOPNOTSUPP; + } + + mutex_unlock(&d->i2c_mutex); + + if (ret < 0) + return ret; + else + return num; +} + +static u32 af9035_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm af9035_i2c_algo = { + .master_xfer = af9035_i2c_master_xfer, + .functionality = af9035_i2c_functionality, +}; + +static int af9035_init(struct dvb_usb_device *d) +{ + int ret, i; + u16 frame_size = 87 * 188 / 4; + u8 packet_size = 512 / 4; + struct reg_val_mask tab[] = { + { 0x80f99d, 0x01, 0x01 }, + { 0x80f9a4, 0x01, 0x01 }, + { 0x00dd11, 0x00, 0x20 }, + { 0x00dd11, 0x00, 0x40 }, + { 0x00dd13, 0x00, 0x20 }, + { 0x00dd13, 0x00, 0x40 }, + { 0x00dd11, 0x20, 0x20 }, + { 0x00dd88, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd89, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0c, packet_size, 0xff}, + { 0x00dd11, af9035_config.dual_mode << 6, 0x40 }, + { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0d, packet_size, 0xff }, + { 0x80f9a3, 0x00, 0x01 }, + { 0x80f9cd, 0x00, 0x01 }, + { 0x80f99d, 0x00, 0x01 }, + { 0x80f9a4, 0x00, 0x01 }, + }; + + pr_debug("%s: USB speed=%d frame_size=%04x packet_size=%02x\n", + __func__, d->udev->speed, frame_size, packet_size); + + /* init endpoints */ + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = af9035_wr_reg_mask(d, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int ret; + u8 wbuf[1] = { 1 }; + u8 rbuf[4]; + struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf, + sizeof(rbuf), rbuf }; + + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + pr_debug("%s: reply=%02x %02x %02x %02x\n", __func__, + rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3]) + *cold = 0; + else + *cold = 1; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_download_firmware(struct usb_device *udev, + const struct firmware *fw) +{ + int ret, i, j, len; + u8 wbuf[1]; + u8 rbuf[4]; + struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL }; + struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; + u8 hdr_core; + u16 hdr_addr, hdr_data_len, hdr_checksum; + #define MAX_DATA 57 + #define HDR_SIZE 7 + + /* + * Thanks to Daniel Glöckner <daniel-gl@gmx.net> about that info! + * + * byte 0: MCS 51 core + * There are two inside the AF9035 (1=Link and 2=OFDM) with separate + * address spaces + * byte 1-2: Big endian destination address + * byte 3-4: Big endian number of data bytes following the header + * byte 5-6: Big endian header checksum, apparently ignored by the chip + * Calculated as ~(h[0]*256+h[1]+h[2]*256+h[3]+h[4]*256) + */ + + for (i = fw->size; i > HDR_SIZE;) { + hdr_core = fw->data[fw->size - i + 0]; + hdr_addr = fw->data[fw->size - i + 1] << 8; + hdr_addr |= fw->data[fw->size - i + 2] << 0; + hdr_data_len = fw->data[fw->size - i + 3] << 8; + hdr_data_len |= fw->data[fw->size - i + 4] << 0; + hdr_checksum = fw->data[fw->size - i + 5] << 8; + hdr_checksum |= fw->data[fw->size - i + 6] << 0; + + pr_debug("%s: core=%d addr=%04x data_len=%d checksum=%04x\n", + __func__, hdr_core, hdr_addr, hdr_data_len, + hdr_checksum); + + if (((hdr_core != 1) && (hdr_core != 2)) || + (hdr_data_len > i)) { + pr_debug("%s: bad firmware\n", __func__); + break; + } + + /* download begin packet */ + req.cmd = CMD_FW_DL_BEGIN; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + /* download firmware packet(s) */ + for (j = HDR_SIZE + hdr_data_len; j > 0; j -= MAX_DATA) { + len = j; + if (len > MAX_DATA) + len = MAX_DATA; + req_fw_dl.wlen = len; + req_fw_dl.wbuf = (u8 *) &fw->data[fw->size - i + + HDR_SIZE + hdr_data_len - j]; + ret = af9035_ctrl_msg(udev, &req_fw_dl); + if (ret < 0) + goto err; + } + + /* download end packet */ + req.cmd = CMD_FW_DL_END; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + i -= hdr_data_len + HDR_SIZE; + + pr_debug("%s: data uploaded=%zu\n", __func__, fw->size - i); + } + + /* firmware loaded, request boot */ + req.cmd = CMD_FW_BOOT; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + /* ensure firmware starts */ + wbuf[0] = 1; + ret = af9035_ctrl_msg(udev, &req_fw_ver); + if (ret < 0) + goto err; + + if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { + info("firmware did not run"); + ret = -ENODEV; + goto err; + } + + info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2], + rbuf[3]); + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_download_firmware_it9135(struct usb_device *udev, + const struct firmware *fw) +{ + int ret, i, i_prev; + u8 wbuf[1]; + u8 rbuf[4]; + struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; + #define HDR_SIZE 7 + + /* + * There seems to be following firmware header. Meaning of bytes 0-3 + * is unknown. + * + * 0: 3 + * 1: 0, 1 + * 2: 0 + * 3: 1, 2, 3 + * 4: addr MSB + * 5: addr LSB + * 6: count of data bytes ? + */ + + for (i = HDR_SIZE, i_prev = 0; i <= fw->size; i++) { + if (i == fw->size || + (fw->data[i + 0] == 0x03 && + (fw->data[i + 1] == 0x00 || + fw->data[i + 1] == 0x01) && + fw->data[i + 2] == 0x00)) { + req_fw_dl.wlen = i - i_prev; + req_fw_dl.wbuf = (u8 *) &fw->data[i_prev]; + i_prev = i; + ret = af9035_ctrl_msg(udev, &req_fw_dl); + if (ret < 0) + goto err; + + pr_debug("%s: data uploaded=%d\n", __func__, i); + } + } + + /* firmware loaded, request boot */ + req.cmd = CMD_FW_BOOT; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + /* ensure firmware starts */ + wbuf[0] = 1; + ret = af9035_ctrl_msg(udev, &req_fw_ver); + if (ret < 0) + goto err; + + if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { + info("firmware did not run"); + ret = -ENODEV; + goto err; + } + + info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2], + rbuf[3]); + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* abuse that callback as there is no better one for reading eeprom */ +static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + int ret, i, eeprom_shift = 0; + u8 tmp; + u16 tmp16; + + /* check if there is dual tuners */ + ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + af9035_config.dual_mode = tmp; + pr_debug("%s: dual mode=%d\n", __func__, af9035_config.dual_mode); + + for (i = 0; i < af9035_properties[0].num_adapters; i++) { + /* tuner */ + ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + af9035_af9033_config[i].tuner = tmp; + pr_debug("%s: [%d]tuner=%02x\n", __func__, i, tmp); + + switch (tmp) { + case AF9033_TUNER_TUA9001: + case AF9033_TUNER_FC0011: + case AF9033_TUNER_MXL5007T: + case AF9033_TUNER_TDA18218: + af9035_af9033_config[i].spec_inv = 1; + break; + default: + af9035_config.hw_not_supported = true; + warn("tuner ID=%02x not supported, please report!", + tmp); + }; + + /* tuner IF frequency */ + ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + tmp16 = tmp; + + ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_H + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + tmp16 |= tmp << 8; + + pr_debug("%s: [%d]IF=%d\n", __func__, i, tmp16); + + eeprom_shift = 0x10; /* shift for the 2nd tuner params */ + } + + /* get demod clock */ + ret = af9035_rd_reg(d, 0x00d800, &tmp); + if (ret < 0) + goto err; + + tmp = (tmp >> 0) & 0x0f; + + for (i = 0; i < af9035_properties[0].num_adapters; i++) + af9035_af9033_config[i].clock = clock_lut[tmp]; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* abuse that callback as there is no better one for reading eeprom */ +static int af9035_read_mac_address_it9135(struct dvb_usb_device *d, u8 mac[6]) +{ + int ret, i; + u8 tmp; + + af9035_config.dual_mode = 0; + + /* get demod clock */ + ret = af9035_rd_reg(d, 0x00d800, &tmp); + if (ret < 0) + goto err; + + tmp = (tmp >> 0) & 0x0f; + + for (i = 0; i < af9035_properties[0].num_adapters; i++) + af9035_af9033_config[i].clock = clock_lut_it9135[tmp]; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + int ret; + + switch (cmd) { + case FC0011_FE_CALLBACK_POWER: + /* Tuner enable */ + ret = af9035_wr_reg_mask(d, 0xd8eb, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ec, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ed, 1, 1); + if (ret < 0) + goto err; + + /* LED */ + ret = af9035_wr_reg_mask(d, 0xd8d0, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8d1, 1, 1); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + break; + case FC0011_FE_CALLBACK_RESET: + ret = af9035_wr_reg(d, 0xd8e9, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0xd8e8, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0xd8e7, 1); + if (ret < 0) + goto err; + + usleep_range(10000, 20000); + + ret = af9035_wr_reg(d, 0xd8e7, 0); + if (ret < 0) + goto err; + + usleep_range(10000, 20000); + break; + default: + ret = -EINVAL; + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +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; + + if (af9035_config.hw_not_supported) { + ret = -ENODEV; + goto err; + } + + if (adap->id == 0) { + ret = af9035_wr_reg(adap->dev, 0x00417f, + af9035_af9033_config[1].i2c_addr); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(adap->dev, 0x00d81a, + af9035_config.dual_mode); + if (ret < 0) + goto err; + } + + /* attach demodulator */ + adap->fe_adap[0].fe = dvb_attach(af9033_attach, + &af9035_af9033_config[adap->id], &adap->dev->i2c_adap); + if (adap->fe_adap[0].fe == NULL) { + ret = -ENODEV; + goto err; + } + + /* disable I2C-gate */ + adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL; + adap->fe_adap[0].fe->callback = af9035_frontend_callback; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static struct tua9001_config af9035_tua9001_config = { + .i2c_addr = 0x60, +}; + +static const struct fc0011_config af9035_fc0011_config = { + .i2c_address = 0x60, +}; + +static struct mxl5007t_config af9035_mxl5007t_config = { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 0, + .clk_out_enable = 0, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, +}; + +static struct tda18218_config af9035_tda18218_config = { + .i2c_address = 0x60, + .i2c_wr_max = 21, +}; + +static int af9035_tuner_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct dvb_frontend *fe; + + switch (af9035_af9033_config[adap->id].tuner) { + case AF9033_TUNER_TUA9001: + /* AF9035 gpiot3 = TUA9001 RESETN + AF9035 gpiot2 = TUA9001 RXEN */ + + /* configure gpiot2 and gpiot2 as output */ + ret = af9035_wr_reg_mask(adap->dev, 0x00d8ec, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8ed, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e8, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e9, 0x01, 0x01); + if (ret < 0) + goto err; + + /* reset tuner */ + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x00, 0x01); + if (ret < 0) + goto err; + + usleep_range(2000, 20000); + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x01, 0x01); + if (ret < 0) + goto err; + + /* activate tuner RX */ + /* TODO: use callback for TUA9001 RXEN */ + ret = af9035_wr_reg_mask(adap->dev, 0x00d8eb, 0x01, 0x01); + if (ret < 0) + goto err; + + /* attach tuner */ + 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; + case AF9033_TUNER_MXL5007T: + ret = af9035_wr_reg(adap->dev, 0x00d8e0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8e1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8df, 0); + if (ret < 0) + goto err; + + msleep(30); + + ret = af9035_wr_reg(adap->dev, 0x00d8df, 1); + if (ret < 0) + goto err; + + msleep(300); + + ret = af9035_wr_reg(adap->dev, 0x00d8c0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8c1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8bf, 0); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8b4, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8b5, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8b3, 1); + if (ret < 0) + goto err; + + /* attach tuner */ + fe = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, 0x60, &af9035_mxl5007t_config); + break; + case AF9033_TUNER_TDA18218: + /* attach tuner */ + fe = dvb_attach(tda18218_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9035_tda18218_config); + break; + default: + fe = NULL; + } + + if (fe == NULL) { + ret = -ENODEV; + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +enum af9035_id_entry { + AF9035_15A4_9035, + AF9035_15A4_1001, + AF9035_0CCD_0093, + AF9035_07CA_A835, + AF9035_07CA_B835, + AF9035_07CA_1867, + AF9035_07CA_A867, + AF9035_07CA_0825, +}; + +static struct usb_device_id af9035_id[] = { + [AF9035_15A4_9035] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035)}, + [AF9035_15A4_1001] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_2)}, + [AF9035_0CCD_0093] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)}, + [AF9035_07CA_A835] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835)}, + [AF9035_07CA_B835] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_B835)}, + [AF9035_07CA_1867] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867)}, + [AF9035_07CA_A867] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A867)}, + [AF9035_07CA_0825] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TWINSTAR)}, + {}, +}; + +MODULE_DEVICE_TABLE(usb, af9035_id); + +static struct dvb_usb_device_properties af9035_properties[] = { + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9035_download_firmware, + .firmware = "dvb-usb-af9035-02.fw", + .no_reconnect = 1, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9035_frontend_attach, + .tuner_attach = af9035_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + .u = { + .bulk = { + .buffersize = (87 * 188), + } + } + } + } + } + } + }, + + .identify_state = af9035_identify_state, + .read_mac_address = af9035_read_mac_address, + + .i2c_algo = &af9035_i2c_algo, + + .num_device_descs = 5, + .devices = { + { + .name = "Afatech AF9035 reference design", + .cold_ids = { + &af9035_id[AF9035_15A4_9035], + &af9035_id[AF9035_15A4_1001], + }, + }, { + .name = "TerraTec Cinergy T Stick", + .cold_ids = { + &af9035_id[AF9035_0CCD_0093], + }, + }, { + .name = "AVerMedia AVerTV Volar HD/PRO (A835)", + .cold_ids = { + &af9035_id[AF9035_07CA_A835], + &af9035_id[AF9035_07CA_B835], + }, + }, { + .name = "AVerMedia HD Volar (A867)", + .cold_ids = { + &af9035_id[AF9035_07CA_1867], + &af9035_id[AF9035_07CA_A867], + }, + }, { + .name = "AVerMedia Twinstar (A825)", + .cold_ids = { + &af9035_id[AF9035_07CA_0825], + }, + }, + } + }, + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9035_download_firmware_it9135, + .firmware = "dvb-usb-it9135-01.fw", + .no_reconnect = 1, + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9035_frontend_attach, + .tuner_attach = af9035_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + .u = { + .bulk = { + .buffersize = (87 * 188), + } + } + } + } + } + } + }, + + .identify_state = af9035_identify_state, + .read_mac_address = af9035_read_mac_address_it9135, + + .i2c_algo = &af9035_i2c_algo, + + .num_device_descs = 0, /* disabled as no support for IT9135 */ + .devices = { + { + .name = "ITE Tech. IT9135 reference design", + }, + } + }, +}; + +static int af9035_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret, i; + struct dvb_usb_device *d = NULL; + struct usb_device *udev; + bool found; + + pr_debug("%s: interface=%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + /* interface 0 is used by DVB-T receiver and + interface 1 is for remote controller (HID) */ + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return 0; + + /* Dynamic USB ID support. Replaces first device ID with current one. */ + udev = interface_to_usbdev(intf); + + for (i = 0, found = false; i < ARRAY_SIZE(af9035_id) - 1; i++) { + if (af9035_id[i].idVendor == + le16_to_cpu(udev->descriptor.idVendor) && + af9035_id[i].idProduct == + le16_to_cpu(udev->descriptor.idProduct)) { + found = true; + break; + } + } + + if (!found) { + pr_debug("%s: using dynamic ID %04x:%04x\n", __func__, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + af9035_properties[0].devices[0].cold_ids[0]->idVendor = + le16_to_cpu(udev->descriptor.idVendor); + af9035_properties[0].devices[0].cold_ids[0]->idProduct = + le16_to_cpu(udev->descriptor.idProduct); + } + + + for (i = 0; i < af9035_properties_count; i++) { + ret = dvb_usb_device_init(intf, &af9035_properties[i], + THIS_MODULE, &d, adapter_nr); + + if (ret == -ENODEV) + continue; + else + break; + } + + if (ret < 0) + goto err; + + if (d) { + ret = af9035_init(d); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver af9035_usb_driver = { + .name = "dvb_usb_af9035", + .probe = af9035_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = af9035_id, +}; + +module_usb_driver(af9035_usb_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Afatech AF9035 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h new file mode 100644 index 000000000000..031bd9cf0cb2 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9035.h @@ -0,0 +1,120 @@ +/* + * Afatech AF9035 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AF9035_H +#define AF9035_H + +/* prefix for dvb-usb log writings */ +#define DVB_USB_LOG_PREFIX "af9035" + +#include "dvb-usb.h" + +struct reg_val { + u32 reg; + u8 val; +}; + +struct reg_val_mask { + u32 reg; + u8 val; + u8 mask; +}; + +struct usb_req { + u8 cmd; + u8 mbox; + u8 wlen; + u8 *wbuf; + u8 rlen; + u8 *rbuf; +}; + +struct config { + bool dual_mode; + bool hw_not_supported; +}; + +struct fw_segment { +#define SEGMENT_FW_DL 0 +#define SEGMENT_ROM_COPY 1 +#define SEGMENT_DIRECT_CMD 2 + u8 type; + u32 len; +}; + +struct fw_header { +#define SEGMENT_MAX_COUNT 6 + u8 segment_count; + struct fw_segment segment[SEGMENT_MAX_COUNT]; +}; + +u32 clock_lut[] = { + 20480000, /* FPGA */ + 16384000, /* 16.38 MHz */ + 20480000, /* 20.48 MHz */ + 36000000, /* 36.00 MHz */ + 30000000, /* 30.00 MHz */ + 26000000, /* 26.00 MHz */ + 28000000, /* 28.00 MHz */ + 32000000, /* 32.00 MHz */ + 34000000, /* 34.00 MHz */ + 24000000, /* 24.00 MHz */ + 22000000, /* 22.00 MHz */ + 12000000, /* 12.00 MHz */ +}; + +u32 clock_lut_it9135[] = { + 12000000, /* 12.00 MHz */ + 20480000, /* 20.48 MHz */ + 36000000, /* 36.00 MHz */ + 30000000, /* 30.00 MHz */ + 26000000, /* 26.00 MHz */ + 28000000, /* 28.00 MHz */ + 32000000, /* 32.00 MHz */ + 34000000, /* 34.00 MHz */ + 24000000, /* 24.00 MHz */ + 22000000, /* 22.00 MHz */ +}; + +/* EEPROM locations */ +#define EEPROM_IR_MODE 0x430d +#define EEPROM_DUAL_MODE 0x4326 +#define EEPROM_IR_TYPE 0x4329 +#define EEPROM_1_IFFREQ_L 0x432d +#define EEPROM_1_IFFREQ_H 0x432e +#define EEPROM_1_TUNER_ID 0x4331 +#define EEPROM_2_IFFREQ_L 0x433d +#define EEPROM_2_IFFREQ_H 0x433e +#define EEPROM_2_TUNER_ID 0x4341 + +/* USB commands */ +#define CMD_MEM_RD 0x00 +#define CMD_MEM_WR 0x01 +#define CMD_I2C_RD 0x02 +#define CMD_I2C_WR 0x03 +#define CMD_FW_DL 0x21 +#define CMD_FW_QUERYINFO 0x22 +#define CMD_FW_BOOT 0x23 +#define CMD_FW_DL_BEGIN 0x24 +#define CMD_FW_DL_END 0x25 +#define CMD_FW_SCATTER_WR 0x29 + +#endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 397d8f232731..94d3f8a5cd9e 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -76,6 +76,8 @@ #define USB_PID_AFATECH_AF9005 0x9020 #define USB_PID_AFATECH_AF9015_9015 0x9015 #define USB_PID_AFATECH_AF9015_9016 0x9016 +#define USB_PID_AFATECH_AF9035 0x9035 +#define USB_PID_AFATECH_AF9035_2 0x1001 #define USB_PID_TREKSTOR_DVBT 0x901b #define USB_VID_ALINK_DTU 0xf170 #define USB_PID_ANSONIC_DVBT_USB 0x6000 @@ -152,6 +154,7 @@ #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 #define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069 +#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093 #define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097 #define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099 #define USB_PID_TWINHAN_VP7041_COLD 0x3201 @@ -221,6 +224,11 @@ #define USB_PID_AVERMEDIA_A850T 0x850b #define USB_PID_AVERMEDIA_A805 0xa805 #define USB_PID_AVERMEDIA_A815M 0x815a +#define USB_PID_AVERMEDIA_A835 0xa835 +#define USB_PID_AVERMEDIA_B835 0xb835 +#define USB_PID_AVERMEDIA_1867 0x1867 +#define USB_PID_AVERMEDIA_A867 0xa867 +#define USB_PID_AVERMEDIA_TWINSTAR 0x0825 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c index 81305de2fea5..91834c583580 100644 --- a/drivers/media/dvb/dvb-usb/mxl111sf.c +++ b/drivers/media/dvb/dvb-usb/mxl111sf.c @@ -340,7 +340,6 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) struct mxl111sf_state *state = d->priv; struct mxl111sf_adap_state *adap_state = adap->fe_adap[adap->active_fe].priv; int ret = 0; - u8 tmp; deb_info("%s(%d)\n", __func__, onoff); diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 21246707fbfb..e11adb64e9e6 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -713,6 +713,11 @@ config DVB_M88RS2000 A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_AF9033 + tristate "Afatech AF9033 DVB-T demodulator" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 86fa808bf589..6ca75578ecac 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -98,4 +98,5 @@ obj-$(CONFIG_DVB_A8293) += a8293.o obj-$(CONFIG_DVB_TDA10071) += tda10071.o obj-$(CONFIG_DVB_RTL2830) += rtl2830.o obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o +obj-$(CONFIG_DVB_AF9033) += af9033.o diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c new file mode 100644 index 000000000000..2cb1f8d6955e --- /dev/null +++ b/drivers/media/dvb/frontends/af9033.c @@ -0,0 +1,919 @@ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "af9033_priv.h" + +struct af9033_state { + struct i2c_adapter *i2c; + struct dvb_frontend fe; + struct af9033_config cfg; + + u32 bandwidth_hz; + bool ts_mode_parallel; + bool ts_mode_serial; +}; + +/* write multiple registers */ +static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val, + int len) +{ + int ret; + u8 buf[3 + len]; + struct i2c_msg msg[1] = { + { + .addr = state->cfg.i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + buf[0] = (reg >> 16) & 0xff; + buf[1] = (reg >> 8) & 0xff; + buf[2] = (reg >> 0) & 0xff; + memcpy(&buf[3], val, len); + + ret = i2c_transfer(state->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + printk(KERN_WARNING "%s: i2c wr failed=%d reg=%06x len=%d\n", + __func__, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* read multiple registers */ +static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len) +{ + int ret; + u8 buf[3] = { (reg >> 16) & 0xff, (reg >> 8) & 0xff, + (reg >> 0) & 0xff }; + struct i2c_msg msg[2] = { + { + .addr = state->cfg.i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf + }, { + .addr = state->cfg.i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = val + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret == 2) { + ret = 0; + } else { + printk(KERN_WARNING "%s: i2c rd failed=%d reg=%06x len=%d\n", + __func__, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + + +/* write single register */ +static int af9033_wr_reg(struct af9033_state *state, u32 reg, u8 val) +{ + return af9033_wr_regs(state, reg, &val, 1); +} + +/* read single register */ +static int af9033_rd_reg(struct af9033_state *state, u32 reg, u8 *val) +{ + return af9033_rd_regs(state, reg, val, 1); +} + +/* write single register with mask */ +static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val, + u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = af9033_rd_regs(state, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return af9033_wr_regs(state, reg, &val, 1); +} + +/* read single register with mask */ +static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val, + u8 mask) +{ + int ret, i; + u8 tmp; + + ret = af9033_rd_regs(state, reg, &tmp, 1); + if (ret) + return ret; + + tmp &= mask; + + /* find position of the first bit */ + for (i = 0; i < 8; i++) { + if ((mask >> i) & 0x01) + break; + } + *val = tmp >> i; + + return 0; +} + +static u32 af9033_div(u32 a, u32 b, u32 x) +{ + u32 r = 0, c = 0, i; + + pr_debug("%s: a=%d b=%d x=%d\n", __func__, a, b, x); + + if (a > b) { + c = a / b; + a = a - c * b; + } + + for (i = 0; i < x; i++) { + if (a >= b) { + r += 1; + a -= b; + } + a <<= 1; + r <<= 1; + } + r = (c << (u32)x) + r; + + pr_debug("%s: a=%d b=%d x=%d r=%d r=%x\n", __func__, a, b, x, r, r); + + return r; +} + +static void af9033_release(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + + kfree(state); +} + +static int af9033_init(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret, i, len; + const struct reg_val *init; + u8 buf[4]; + u32 adc_cw, clock_cw; + struct reg_val_mask tab[] = { + { 0x80fb24, 0x00, 0x08 }, + { 0x80004c, 0x00, 0xff }, + { 0x00f641, state->cfg.tuner, 0xff }, + { 0x80f5ca, 0x01, 0x01 }, + { 0x80f715, 0x01, 0x01 }, + { 0x00f41f, 0x04, 0x04 }, + { 0x00f41a, 0x01, 0x01 }, + { 0x80f731, 0x00, 0x01 }, + { 0x00d91e, 0x00, 0x01 }, + { 0x00d919, 0x00, 0x01 }, + { 0x80f732, 0x00, 0x01 }, + { 0x00d91f, 0x00, 0x01 }, + { 0x00d91a, 0x00, 0x01 }, + { 0x80f730, 0x00, 0x01 }, + { 0x80f778, 0x00, 0xff }, + { 0x80f73c, 0x01, 0x01 }, + { 0x80f776, 0x00, 0x01 }, + { 0x00d8fd, 0x01, 0xff }, + { 0x00d830, 0x01, 0xff }, + { 0x00d831, 0x00, 0xff }, + { 0x00d832, 0x00, 0xff }, + { 0x80f985, state->ts_mode_serial, 0x01 }, + { 0x80f986, state->ts_mode_parallel, 0x01 }, + { 0x00d827, 0x00, 0xff }, + { 0x00d829, 0x00, 0xff }, + }; + + /* program clock control */ + clock_cw = af9033_div(state->cfg.clock, 1000000ul, 19ul); + buf[0] = (clock_cw >> 0) & 0xff; + buf[1] = (clock_cw >> 8) & 0xff; + buf[2] = (clock_cw >> 16) & 0xff; + buf[3] = (clock_cw >> 24) & 0xff; + + pr_debug("%s: clock=%d clock_cw=%08x\n", __func__, state->cfg.clock, + clock_cw); + + ret = af9033_wr_regs(state, 0x800025, buf, 4); + if (ret < 0) + goto err; + + /* program ADC control */ + for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { + if (clock_adc_lut[i].clock == state->cfg.clock) + break; + } + + adc_cw = af9033_div(clock_adc_lut[i].adc, 1000000ul, 19ul); + buf[0] = (adc_cw >> 0) & 0xff; + buf[1] = (adc_cw >> 8) & 0xff; + buf[2] = (adc_cw >> 16) & 0xff; + + pr_debug("%s: adc=%d adc_cw=%06x\n", __func__, clock_adc_lut[i].adc, + adc_cw); + + ret = af9033_wr_regs(state, 0x80f1cd, buf, 3); + if (ret < 0) + goto err; + + /* program register table */ + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = af9033_wr_reg_mask(state, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret < 0) + goto err; + } + + /* settings for TS interface */ + if (state->cfg.ts_mode == AF9033_TS_MODE_USB) { + ret = af9033_wr_reg_mask(state, 0x80f9a5, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x01, 0x01); + if (ret < 0) + goto err; + } else { + ret = af9033_wr_reg_mask(state, 0x80f990, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x00, 0x01); + if (ret < 0) + goto err; + } + + /* load OFSM settings */ + pr_debug("%s: load ofsm settings\n", __func__); + len = ARRAY_SIZE(ofsm_init); + init = ofsm_init; + for (i = 0; i < len; i++) { + ret = af9033_wr_reg(state, init[i].reg, init[i].val); + if (ret < 0) + goto err; + } + + /* load tuner specific settings */ + pr_debug("%s: load tuner specific settings\n", + __func__); + switch (state->cfg.tuner) { + case AF9033_TUNER_TUA9001: + 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; + case AF9033_TUNER_MXL5007T: + len = ARRAY_SIZE(tuner_init_mxl5007t); + init = tuner_init_mxl5007t; + break; + case AF9033_TUNER_TDA18218: + len = ARRAY_SIZE(tuner_init_tda18218); + init = tuner_init_tda18218; + break; + default: + pr_debug("%s: unsupported tuner ID=%d\n", __func__, + state->cfg.tuner); + ret = -ENODEV; + goto err; + } + + for (i = 0; i < len; i++) { + ret = af9033_wr_reg(state, init[i].reg, init[i].val); + if (ret < 0) + goto err; + } + + state->bandwidth_hz = 0; /* force to program all parameters */ + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_sleep(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret, i; + u8 tmp; + + ret = af9033_wr_reg(state, 0x80004c, 1); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800000, 0); + if (ret < 0) + goto err; + + for (i = 100, tmp = 1; i && tmp; i--) { + ret = af9033_rd_reg(state, 0x80004c, &tmp); + if (ret < 0) + goto err; + + usleep_range(200, 10000); + } + + pr_debug("%s: loop=%d\n", __func__, i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto err; + } + + ret = af9033_wr_reg_mask(state, 0x80fb24, 0x08, 0x08); + if (ret < 0) + goto err; + + /* prevent current leak (?) */ + if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + /* enable parallel TS */ + ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x00d916, 0x01, 0x01); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 800; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int af9033_set_frontend(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, spec_inv; + u8 tmp, buf[3], bandwidth_reg_val; + u32 if_frequency, freq_cw, adc_freq; + + pr_debug("%s: frequency=%d bandwidth_hz=%d\n", __func__, c->frequency, + c->bandwidth_hz); + + /* check bandwidth */ + switch (c->bandwidth_hz) { + case 6000000: + bandwidth_reg_val = 0x00; + break; + case 7000000: + bandwidth_reg_val = 0x01; + break; + case 8000000: + bandwidth_reg_val = 0x02; + break; + default: + pr_debug("%s: invalid bandwidth_hz\n", __func__); + ret = -EINVAL; + goto err; + } + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* program CFOE coefficients */ + if (c->bandwidth_hz != state->bandwidth_hz) { + for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) { + if (coeff_lut[i].clock == state->cfg.clock && + coeff_lut[i].bandwidth_hz == c->bandwidth_hz) { + break; + } + } + ret = af9033_wr_regs(state, 0x800001, + coeff_lut[i].val, sizeof(coeff_lut[i].val)); + } + + /* program frequency control */ + if (c->bandwidth_hz != state->bandwidth_hz) { + spec_inv = state->cfg.spec_inv ? -1 : 1; + + for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { + if (clock_adc_lut[i].clock == state->cfg.clock) + break; + } + adc_freq = clock_adc_lut[i].adc; + + /* get used IF frequency */ + if (fe->ops.tuner_ops.get_if_frequency) + fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + else + if_frequency = 0; + + while (if_frequency > (adc_freq / 2)) + if_frequency -= adc_freq; + + if (if_frequency >= 0) + spec_inv *= -1; + else + if_frequency *= -1; + + freq_cw = af9033_div(if_frequency, adc_freq, 23ul); + + if (spec_inv == -1) + freq_cw *= -1; + + /* get adc multiplies */ + ret = af9033_rd_reg(state, 0x800045, &tmp); + if (ret < 0) + goto err; + + if (tmp == 1) + freq_cw /= 2; + + buf[0] = (freq_cw >> 0) & 0xff; + buf[1] = (freq_cw >> 8) & 0xff; + buf[2] = (freq_cw >> 16) & 0x7f; + ret = af9033_wr_regs(state, 0x800029, buf, 3); + if (ret < 0) + goto err; + + state->bandwidth_hz = c->bandwidth_hz; + } + + ret = af9033_wr_reg_mask(state, 0x80f904, bandwidth_reg_val, 0x03); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800040, 0x00); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800047, 0x00); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x80f999, 0x00, 0x01); + if (ret < 0) + goto err; + + if (c->frequency <= 230000000) + tmp = 0x00; /* VHF */ + else + tmp = 0x01; /* UHF */ + + ret = af9033_wr_reg(state, 0x80004b, tmp); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800000, 0x00); + if (ret < 0) + goto err; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_get_frontend(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + u8 buf[8]; + + pr_debug("%s\n", __func__); + + /* read all needed registers */ + ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf)); + if (ret < 0) + goto err; + + switch ((buf[0] >> 0) & 3) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + switch ((buf[1] >> 0) & 3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[2] >> 0) & 7) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[3] >> 0) & 3) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + } + + switch ((buf[4] >> 0) & 3) { + case 0: + c->bandwidth_hz = 6000000; + break; + case 1: + c->bandwidth_hz = 7000000; + break; + case 2: + c->bandwidth_hz = 8000000; + break; + } + + switch ((buf[6] >> 0) & 7) { + case 0: + c->code_rate_HP = FEC_1_2; + break; + case 1: + c->code_rate_HP = FEC_2_3; + break; + case 2: + c->code_rate_HP = FEC_3_4; + break; + case 3: + c->code_rate_HP = FEC_5_6; + break; + case 4: + c->code_rate_HP = FEC_7_8; + break; + case 5: + c->code_rate_HP = FEC_NONE; + break; + } + + switch ((buf[7] >> 0) & 7) { + case 0: + c->code_rate_LP = FEC_1_2; + break; + case 1: + c->code_rate_LP = FEC_2_3; + break; + case 2: + c->code_rate_LP = FEC_3_4; + break; + case 3: + c->code_rate_LP = FEC_5_6; + break; + case 4: + c->code_rate_LP = FEC_7_8; + break; + case 5: + c->code_rate_LP = FEC_NONE; + break; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + u8 tmp; + + *status = 0; + + /* radio channel status, 0=no result, 1=has signal, 2=no signal */ + ret = af9033_rd_reg(state, 0x800047, &tmp); + if (ret < 0) + goto err; + + /* has signal */ + if (tmp == 0x01) + *status |= FE_HAS_SIGNAL; + + if (tmp != 0x02) { + /* TPS lock */ + ret = af9033_rd_reg_mask(state, 0x80f5a9, &tmp, 0x01); + if (ret < 0) + goto err; + + if (tmp) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + + /* full lock */ + ret = af9033_rd_reg_mask(state, 0x80f999, &tmp, 0x01); + if (ret < 0) + goto err; + + if (tmp) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret, i, len; + u8 buf[3], tmp; + u32 snr_val; + const struct val_snr *uninitialized_var(snr_lut); + + /* read value */ + ret = af9033_rd_regs(state, 0x80002c, buf, 3); + if (ret < 0) + goto err; + + snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + /* read current modulation */ + ret = af9033_rd_reg(state, 0x80f903, &tmp); + if (ret < 0) + goto err; + + switch ((tmp >> 0) & 3) { + case 0: + len = ARRAY_SIZE(qpsk_snr_lut); + snr_lut = qpsk_snr_lut; + break; + case 1: + len = ARRAY_SIZE(qam16_snr_lut); + snr_lut = qam16_snr_lut; + break; + case 2: + len = ARRAY_SIZE(qam64_snr_lut); + snr_lut = qam64_snr_lut; + break; + default: + goto err; + } + + for (i = 0; i < len; i++) { + tmp = snr_lut[i].snr; + + if (snr_val < snr_lut[i].val) + break; + } + + *snr = tmp * 10; /* dB/10 */ + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + u8 strength2; + + /* read signal strength of 0-100 scale */ + ret = af9033_rd_reg(state, 0x800048, &strength2); + if (ret < 0) + goto err; + + /* scale value to 0x0000-0xffff */ + *strength = strength2 * 0xffff / 100; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + *ber = 0; + + return 0; +} + +static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + + return 0; +} + +static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + + pr_debug("%s: enable=%d\n", __func__, enable); + + ret = af9033_wr_reg_mask(state, 0x00fa04, enable, 0x01); + if (ret < 0) + goto err; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static struct dvb_frontend_ops af9033_ops; + +struct dvb_frontend *af9033_attach(const struct af9033_config *config, + struct i2c_adapter *i2c) +{ + int ret; + struct af9033_state *state; + u8 buf[8]; + + pr_debug("%s:\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct af9033_state), GFP_KERNEL); + if (state == NULL) + goto err; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->cfg, config, sizeof(struct af9033_config)); + + if (state->cfg.clock != 12000000) { + printk(KERN_INFO "af9033: unsupported clock=%d, only " \ + "12000000 Hz is supported currently\n", + state->cfg.clock); + goto err; + } + + /* firmware version */ + ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4); + if (ret < 0) + goto err; + + ret = af9033_rd_regs(state, 0x804191, &buf[4], 4); + if (ret < 0) + goto err; + + printk(KERN_INFO "af9033: firmware version: LINK=%d.%d.%d.%d " \ + "OFDM=%d.%d.%d.%d\n", buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + /* configure internal TS mode */ + switch (state->cfg.ts_mode) { + case AF9033_TS_MODE_PARALLEL: + state->ts_mode_parallel = true; + break; + case AF9033_TS_MODE_SERIAL: + state->ts_mode_serial = true; + break; + case AF9033_TS_MODE_USB: + /* usb mode for AF9035 */ + default: + break; + } + + /* create dvb_frontend */ + memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops)); + state->fe.demodulator_priv = state; + + return &state->fe; + +err: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(af9033_attach); + +static struct dvb_frontend_ops af9033_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Afatech AF9033 (DVB-T)", + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 250000, + .frequency_tolerance = 0, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .release = af9033_release, + + .init = af9033_init, + .sleep = af9033_sleep, + + .get_tune_settings = af9033_get_tune_settings, + .set_frontend = af9033_set_frontend, + .get_frontend = af9033_get_frontend, + + .read_status = af9033_read_status, + .read_snr = af9033_read_snr, + .read_signal_strength = af9033_read_signal_strength, + .read_ber = af9033_read_ber, + .read_ucblocks = af9033_read_ucblocks, + + .i2c_gate_ctrl = af9033_i2c_gate_ctrl, +}; + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/af9033.h b/drivers/media/dvb/frontends/af9033.h new file mode 100644 index 000000000000..9e302c3f0f7d --- /dev/null +++ b/drivers/media/dvb/frontends/af9033.h @@ -0,0 +1,75 @@ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AF9033_H +#define AF9033_H + +struct af9033_config { + /* + * I2C address + */ + u8 i2c_addr; + + /* + * clock Hz + * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000, + * 30000000, 36000000, 20480000, 16384000 + */ + u32 clock; + + /* + * tuner + */ +#define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ +#define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ +#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ +#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ + u8 tuner; + + /* + * TS settings + */ +#define AF9033_TS_MODE_USB 0 +#define AF9033_TS_MODE_PARALLEL 1 +#define AF9033_TS_MODE_SERIAL 2 + u8 ts_mode:2; + + /* + * input spectrum inversion + */ + bool spec_inv; +}; + + +#if defined(CONFIG_DVB_AF9033) || \ + (defined(CONFIG_DVB_AF9033_MODULE) && defined(MODULE)) +extern struct dvb_frontend *af9033_attach(const struct af9033_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *af9033_attach( + const struct af9033_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* AF9033_H */ diff --git a/drivers/media/dvb/frontends/af9033_priv.h b/drivers/media/dvb/frontends/af9033_priv.h new file mode 100644 index 000000000000..0b783b9ed75e --- /dev/null +++ b/drivers/media/dvb/frontends/af9033_priv.h @@ -0,0 +1,470 @@ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * Copyright (C) 2012 Antti Palosaari <crope@iki.fi> + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AF9033_PRIV_H +#define AF9033_PRIV_H + +#include "dvb_frontend.h" +#include "af9033.h" + +struct reg_val { + u32 reg; + u8 val; +}; + +struct reg_val_mask { + u32 reg; + u8 val; + u8 mask; +}; + +struct coeff { + u32 clock; + u32 bandwidth_hz; + u8 val[36]; +}; + +struct clock_adc { + u32 clock; + u32 adc; +}; + +struct val_snr { + u32 val; + u8 snr; +}; + +/* Xtal clock vs. ADC clock lookup table */ +static const struct clock_adc clock_adc_lut[] = { + { 16384000, 20480000 }, + { 20480000, 20480000 }, + { 36000000, 20250000 }, + { 30000000, 20156250 }, + { 26000000, 20583333 }, + { 28000000, 20416667 }, + { 32000000, 20500000 }, + { 34000000, 20187500 }, + { 24000000, 20500000 }, + { 22000000, 20625000 }, + { 12000000, 20250000 }, +}; + +/* pre-calculated coeff lookup table */ +static const struct coeff coeff_lut[] = { + /* 12.000 MHz */ + { 12000000, 8000000, { + 0x01, 0xce, 0x55, 0xc9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, + 0x99, 0x0f, 0x00, 0x73, 0x95, 0x72, 0x00, 0x73, 0x91, 0xd5, + 0x00, 0x39, 0xca, 0xb9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, + 0x95, 0x72, 0x37, 0x02, 0xce, 0x01 } + }, + { 12000000, 7000000, { + 0x01, 0x94, 0x8b, 0x10, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, + 0x25, 0xed, 0x00, 0x65, 0x22, 0xc4, 0x00, 0x65, 0x1f, 0x9b, + 0x00, 0x32, 0x91, 0x62, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, + 0x22, 0xc4, 0x88, 0x02, 0x95, 0x01 } + }, + { 12000000, 6000000, { + 0x01, 0x5a, 0xc0, 0x56, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, + 0xb2, 0xcb, 0x00, 0x56, 0xb0, 0x15, 0x00, 0x56, 0xad, 0x60, + 0x00, 0x2b, 0x58, 0x0b, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, + 0xb0, 0x15, 0xf4, 0x02, 0x5b, 0x01 } + }, +}; + +/* QPSK SNR lookup table */ +static const struct val_snr qpsk_snr_lut[] = { + { 0x0b4771, 0 }, + { 0x0c1aed, 1 }, + { 0x0d0d27, 2 }, + { 0x0e4d19, 3 }, + { 0x0e5da8, 4 }, + { 0x107097, 5 }, + { 0x116975, 6 }, + { 0x1252d9, 7 }, + { 0x131fa4, 8 }, + { 0x13d5e1, 9 }, + { 0x148e53, 10 }, + { 0x15358b, 11 }, + { 0x15dd29, 12 }, + { 0x168112, 13 }, + { 0x170b61, 14 }, + { 0x17a532, 15 }, + { 0x180f94, 16 }, + { 0x186ed2, 17 }, + { 0x18b271, 18 }, + { 0x18e118, 19 }, + { 0x18ff4b, 20 }, + { 0x190af1, 21 }, + { 0x191451, 22 }, + { 0xffffff, 23 }, +}; + +/* QAM16 SNR lookup table */ +static const struct val_snr qam16_snr_lut[] = { + { 0x04f0d5, 0 }, + { 0x05387a, 1 }, + { 0x0573a4, 2 }, + { 0x05a99e, 3 }, + { 0x05cc80, 4 }, + { 0x05eb62, 5 }, + { 0x05fecf, 6 }, + { 0x060b80, 7 }, + { 0x062501, 8 }, + { 0x064865, 9 }, + { 0x069604, 10 }, + { 0x06f356, 11 }, + { 0x07706a, 12 }, + { 0x0804d3, 13 }, + { 0x089d1a, 14 }, + { 0x093e3d, 15 }, + { 0x09e35d, 16 }, + { 0x0a7c3c, 17 }, + { 0x0afaf8, 18 }, + { 0x0b719d, 19 }, + { 0x0bda6a, 20 }, + { 0x0c0c75, 21 }, + { 0x0c3f7d, 22 }, + { 0x0c5e62, 23 }, + { 0x0c6c31, 24 }, + { 0x0c7925, 25 }, + { 0xffffff, 26 }, +}; + +/* QAM64 SNR lookup table */ +static const struct val_snr qam64_snr_lut[] = { + { 0x0256d0, 0 }, + { 0x027a65, 1 }, + { 0x029873, 2 }, + { 0x02b7fe, 3 }, + { 0x02cf1e, 4 }, + { 0x02e234, 5 }, + { 0x02f409, 6 }, + { 0x030046, 7 }, + { 0x030844, 8 }, + { 0x030a02, 9 }, + { 0x030cde, 10 }, + { 0x031031, 11 }, + { 0x03144c, 12 }, + { 0x0315dd, 13 }, + { 0x031920, 14 }, + { 0x0322d0, 15 }, + { 0x0339fc, 16 }, + { 0x0364a1, 17 }, + { 0x038bcc, 18 }, + { 0x03c7d3, 19 }, + { 0x0408cc, 20 }, + { 0x043bed, 21 }, + { 0x048061, 22 }, + { 0x04be95, 23 }, + { 0x04fa7d, 24 }, + { 0x052405, 25 }, + { 0x05570d, 26 }, + { 0x059feb, 27 }, + { 0x05bf38, 28 }, + { 0xffffff, 29 }, +}; + +static const struct reg_val ofsm_init[] = { + { 0x800051, 0x01 }, + { 0x800070, 0x0a }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800099, 0x01 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000ab, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000c0, 0x05 }, + { 0x8000c4, 0x19 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* Infineon TUA 9001 tuner init + AF9033_TUNER_TUA9001 = 0x27 */ +static const struct reg_val tuner_init_tua9001[] = { + { 0x800046, 0x27 }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x80006d, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800075, 0x03 }, + { 0x800076, 0x02 }, + { 0x800077, 0x00 }, + { 0x800078, 0x01 }, + { 0x800079, 0x00 }, + { 0x80007a, 0x7e }, + { 0x80007b, 0x3e }, + { 0x800093, 0x00 }, + { 0x800094, 0x01 }, + { 0x800095, 0x02 }, + { 0x800096, 0x01 }, + { 0x800098, 0x0a }, + { 0x80009b, 0x05 }, + { 0x80009c, 0x80 }, + { 0x8000b3, 0x00 }, + { 0x8000c1, 0x01 }, + { 0x8000c2, 0x00 }, + { 0x80f007, 0x00 }, + { 0x80f01f, 0x82 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 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 }, +}; + +/* MaxLinear MxL5007T tuner init + AF9033_TUNER_MXL5007T = 0xa0 */ +static const struct reg_val tuner_init_mxl5007t[] = { + { 0x800046, 0x1b }, + { 0x800057, 0x01 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x96 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800079, 0x01 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x8000b3, 0x01 }, + { 0x8000c1, 0x01 }, + { 0x8000c2, 0x00 }, + { 0x80f007, 0x00 }, + { 0x80f00c, 0x19 }, + { 0x80f00d, 0x1a }, + { 0x80f012, 0xda }, + { 0x80f013, 0x00 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01f, 0x82 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f077, 0x02 }, + { 0x80f1e6, 0x00 }, +}; + +/* NXP TDA 18218HN tuner init + AF9033_TUNER_TDA18218 = 0xa1 */ +static const struct reg_val tuner_init_tda18218[] = { + {0x800046, 0xa1}, + {0x800057, 0x01}, + {0x800058, 0x01}, + {0x80005f, 0x00}, + {0x800060, 0x00}, + {0x800071, 0x05}, + {0x800072, 0x02}, + {0x800074, 0x01}, + {0x800079, 0x01}, + {0x800093, 0x00}, + {0x800094, 0x00}, + {0x800095, 0x00}, + {0x800096, 0x00}, + {0x8000b3, 0x01}, + {0x8000c3, 0x01}, + {0x8000c4, 0x00}, + {0x80f007, 0x00}, + {0x80f00c, 0x19}, + {0x80f00d, 0x1a}, + {0x80f012, 0xda}, + {0x80f013, 0x00}, + {0x80f014, 0x00}, + {0x80f015, 0x02}, + {0x80f01f, 0x82}, + {0x80f020, 0x00}, + {0x80f029, 0x82}, + {0x80f02a, 0x00}, + {0x80f077, 0x02}, + {0x80f1e6, 0x00}, +}; + +#endif /* AF9033_PRIV_H */ + diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 2e639ce6f256..235c0e349820 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -29,6 +29,7 @@ #include <linux/videodev2.h> /* kernel radio structs */ #include <linux/mutex.h> #include <linux/io.h> /* outb, outb_p */ +#include <linux/pnp.h> #include <linux/slab.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> @@ -283,6 +284,16 @@ static const struct radio_isa_ops gemtek_ops = { static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; +#ifdef CONFIG_PNP +static struct pnp_device_id gemtek_pnp_devices[] = { + /* AOpen FX-3D/Pro Radio */ + {.id = "ADS7183", .driver_data = 0}, + {.id = ""} +}; + +MODULE_DEVICE_TABLE(pnp, gemtek_pnp_devices); +#endif + static struct radio_isa_driver gemtek_driver = { .driver = { .match = radio_isa_match, @@ -292,6 +303,14 @@ static struct radio_isa_driver gemtek_driver = { .name = "radio-gemtek", }, }, +#ifdef CONFIG_PNP + .pnp_driver = { + .name = "radio-gemtek", + .id_table = gemtek_pnp_devices, + .probe = radio_isa_pnp_probe, + .remove = radio_isa_pnp_remove, + }, +#endif .io_params = io, .radio_nr_params = radio_nr, .io_ports = gemtek_ioports, @@ -305,12 +324,18 @@ static struct radio_isa_driver gemtek_driver = { static int __init gemtek_init(void) { gemtek_driver.probe = probe; +#ifdef CONFIG_PNP + pnp_register_driver(&gemtek_driver.pnp_driver); +#endif return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX); } static void __exit gemtek_exit(void) { hardmute = 1; /* Turn off PLL */ +#ifdef CONFIG_PNP + pnp_unregister_driver(&gemtek_driver.pnp_driver); +#endif isa_unregister_driver(&gemtek_driver.driver); } diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c index 06f906351fad..ed9039f8571c 100644 --- a/drivers/media/radio/radio-isa.c +++ b/drivers/media/radio/radio-isa.c @@ -199,56 +199,31 @@ static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io) return false; } -int radio_isa_probe(struct device *pdev, unsigned int dev) +struct radio_isa_card *radio_isa_alloc(struct radio_isa_driver *drv, + struct device *pdev) { - struct radio_isa_driver *drv = pdev->platform_data; - const struct radio_isa_ops *ops = drv->ops; struct v4l2_device *v4l2_dev; - struct radio_isa_card *isa; - int res; + struct radio_isa_card *isa = drv->ops->alloc(); + if (!isa) + return NULL; - isa = drv->ops->alloc(); - if (isa == NULL) - return -ENOMEM; dev_set_drvdata(pdev, isa); isa->drv = drv; - isa->io = drv->io_params[dev]; v4l2_dev = &isa->v4l2_dev; strlcpy(v4l2_dev->name, dev_name(pdev), sizeof(v4l2_dev->name)); - if (drv->probe && ops->probe) { - int i; - - for (i = 0; i < drv->num_of_io_ports; ++i) { - int io = drv->io_ports[i]; - - if (request_region(io, drv->region_size, v4l2_dev->name)) { - bool found = ops->probe(isa, io); - - release_region(io, drv->region_size); - if (found) { - isa->io = io; - break; - } - } - } - } - - if (!radio_isa_valid_io(drv, isa->io)) { - int i; + return isa; +} - if (isa->io < 0) - return -ENODEV; - v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x", - drv->io_ports[0]); - for (i = 1; i < drv->num_of_io_ports; i++) - printk(KERN_CONT "/0x%03x", drv->io_ports[i]); - printk(KERN_CONT ".\n"); - kfree(isa); - return -EINVAL; - } +int radio_isa_common_probe(struct radio_isa_card *isa, struct device *pdev, + int radio_nr, unsigned region_size) +{ + const struct radio_isa_driver *drv = isa->drv; + const struct radio_isa_ops *ops = drv->ops; + struct v4l2_device *v4l2_dev = &isa->v4l2_dev; + int res; - if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) { + if (!request_region(isa->io, region_size, v4l2_dev->name)) { v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io); kfree(isa); return -EBUSY; @@ -301,8 +276,8 @@ int radio_isa_probe(struct device *pdev, unsigned int dev) v4l2_err(v4l2_dev, "Could not setup card\n"); goto err_node_reg; } - res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, - drv->radio_nr_params[dev]); + res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, radio_nr); + if (res < 0) { v4l2_err(v4l2_dev, "Could not register device node\n"); goto err_node_reg; @@ -317,24 +292,110 @@ err_node_reg: err_hdl: v4l2_device_unregister(&isa->v4l2_dev); err_dev_reg: - release_region(isa->io, drv->region_size); + release_region(isa->io, region_size); kfree(isa); return res; } -EXPORT_SYMBOL_GPL(radio_isa_probe); -int radio_isa_remove(struct device *pdev, unsigned int dev) +int radio_isa_common_remove(struct radio_isa_card *isa, unsigned region_size) { - struct radio_isa_card *isa = dev_get_drvdata(pdev); const struct radio_isa_ops *ops = isa->drv->ops; ops->s_mute_volume(isa, true, isa->volume ? isa->volume->cur.val : 0); video_unregister_device(&isa->vdev); v4l2_ctrl_handler_free(&isa->hdl); v4l2_device_unregister(&isa->v4l2_dev); - release_region(isa->io, isa->drv->region_size); + release_region(isa->io, region_size); v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card); kfree(isa); return 0; } + +int radio_isa_probe(struct device *pdev, unsigned int dev) +{ + struct radio_isa_driver *drv = pdev->platform_data; + const struct radio_isa_ops *ops = drv->ops; + struct v4l2_device *v4l2_dev; + struct radio_isa_card *isa; + + isa = radio_isa_alloc(drv, pdev); + if (!isa) + return -ENOMEM; + isa->io = drv->io_params[dev]; + v4l2_dev = &isa->v4l2_dev; + + if (drv->probe && ops->probe) { + int i; + + for (i = 0; i < drv->num_of_io_ports; ++i) { + int io = drv->io_ports[i]; + + if (request_region(io, drv->region_size, v4l2_dev->name)) { + bool found = ops->probe(isa, io); + + release_region(io, drv->region_size); + if (found) { + isa->io = io; + break; + } + } + } + } + + if (!radio_isa_valid_io(drv, isa->io)) { + int i; + + if (isa->io < 0) + return -ENODEV; + v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x", + drv->io_ports[0]); + for (i = 1; i < drv->num_of_io_ports; i++) + printk(KERN_CONT "/0x%03x", drv->io_ports[i]); + printk(KERN_CONT ".\n"); + kfree(isa); + return -EINVAL; + } + + return radio_isa_common_probe(isa, pdev, drv->radio_nr_params[dev], + drv->region_size); +} +EXPORT_SYMBOL_GPL(radio_isa_probe); + +int radio_isa_remove(struct device *pdev, unsigned int dev) +{ + struct radio_isa_card *isa = dev_get_drvdata(pdev); + + return radio_isa_common_remove(isa, isa->drv->region_size); +} EXPORT_SYMBOL_GPL(radio_isa_remove); + +#ifdef CONFIG_PNP +int radio_isa_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + struct pnp_driver *pnp_drv = to_pnp_driver(dev->dev.driver); + struct radio_isa_driver *drv = container_of(pnp_drv, + struct radio_isa_driver, pnp_driver); + struct radio_isa_card *isa; + + if (!pnp_port_valid(dev, 0)) + return -ENODEV; + + isa = radio_isa_alloc(drv, &dev->dev); + if (!isa) + return -ENOMEM; + + isa->io = pnp_port_start(dev, 0); + + return radio_isa_common_probe(isa, &dev->dev, drv->radio_nr_params[0], + pnp_port_len(dev, 0)); +} +EXPORT_SYMBOL_GPL(radio_isa_pnp_probe); + +void radio_isa_pnp_remove(struct pnp_dev *dev) +{ + struct radio_isa_card *isa = dev_get_drvdata(&dev->dev); + + radio_isa_common_remove(isa, pnp_port_len(dev, 0)); +} +EXPORT_SYMBOL_GPL(radio_isa_pnp_remove); +#endif diff --git a/drivers/media/radio/radio-isa.h b/drivers/media/radio/radio-isa.h index 8a0ea84d86de..ba4c01f1bd0c 100644 --- a/drivers/media/radio/radio-isa.h +++ b/drivers/media/radio/radio-isa.h @@ -24,6 +24,7 @@ #define _RADIO_ISA_H_ #include <linux/isa.h> +#include <linux/pnp.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> @@ -76,6 +77,9 @@ struct radio_isa_ops { /* Top level structure needed to instantiate the cards */ struct radio_isa_driver { struct isa_driver driver; +#ifdef CONFIG_PNP + struct pnp_driver pnp_driver; +#endif const struct radio_isa_ops *ops; /* The module_param_array with the specified I/O ports */ int *io_params; @@ -101,5 +105,10 @@ struct radio_isa_driver { int radio_isa_match(struct device *pdev, unsigned int dev); int radio_isa_probe(struct device *pdev, unsigned int dev); int radio_isa_remove(struct device *pdev, unsigned int dev); +#ifdef CONFIG_PNP +int radio_isa_pnp_probe(struct pnp_dev *dev, + const struct pnp_device_id *dev_id); +void radio_isa_pnp_remove(struct pnp_dev *dev); +#endif #endif diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index b275c5d0fe9a..b1f844c64fde 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -17,6 +17,7 @@ #include <linux/videodev2.h> /* kernel radio structs */ #include <linux/mutex.h> #include <linux/io.h> /* outb, outb_p */ +#include <linux/slab.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include "radio-isa.h" diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index baf907b3ce76..26fa043d3de7 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -1,7 +1,7 @@ /* * USB ATI Remote support * - * Copyright (c) 2011 Anssi Hannula <anssi.hannula@iki.fi> + * Copyright (c) 2011, 2012 Anssi Hannula <anssi.hannula@iki.fi> * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net> * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev * @@ -151,13 +151,35 @@ MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes"); #undef err #define err(format, arg...) printk(KERN_ERR format , ## arg) +struct ati_receiver_type { + /* either default_keymap or get_default_keymap should be set */ + const char *default_keymap; + const char *(*get_default_keymap)(struct usb_interface *interface); +}; + +static const char *get_medion_keymap(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + /* The receiver shipped with the "Digitainer" variant helpfully has + * a single additional bit set in its descriptor. */ + if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) + return RC_MAP_MEDION_X10_DIGITAINER; + + return RC_MAP_MEDION_X10; +} + +static const struct ati_receiver_type type_ati = { .default_keymap = RC_MAP_ATI_X10 }; +static const struct ati_receiver_type type_medion = { .get_default_keymap = get_medion_keymap }; +static const struct ati_receiver_type type_firefly = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY }; + static struct usb_device_id ati_remote_table[] = { - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_MEDION_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_SNAPSTREAM_FIREFLY }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_medion }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_firefly }, {} /* Terminating entry */ }; @@ -445,6 +467,7 @@ static void ati_remote_input_report(struct urb *urb) int acc; int remote_num; unsigned char scancode; + u32 wheel_keycode = KEY_RESERVED; int i; /* @@ -484,26 +507,33 @@ static void ati_remote_input_report(struct urb *urb) */ scancode = data[2] & 0x7f; - /* Look up event code index in the mouse translation table. */ - for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { - if (scancode == ati_remote_tbl[i].data) { - index = i; - break; + dbginfo(&ati_remote->interface->dev, + "channel 0x%02x; key data %02x, scancode %02x\n", + remote_num, data[2], scancode); + + if (scancode >= 0x70) { + /* + * This is either a mouse or scrollwheel event, depending on + * the remote/keymap. + * Get the keycode assigned to scancode 0x78/0x70. If it is + * set, assume this is a scrollwheel up/down event. + */ + wheel_keycode = rc_g_keycode_from_table(ati_remote->rdev, + scancode & 0x78); + + if (wheel_keycode == KEY_RESERVED) { + /* scrollwheel was not mapped, assume mouse */ + + /* Look up event code index in the mouse translation table. */ + for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { + if (scancode == ati_remote_tbl[i].data) { + index = i; + break; + } + } } } - if (index >= 0) { - dbginfo(&ati_remote->interface->dev, - "channel 0x%02x; mouse data %02x; index %d; keycode %d\n", - remote_num, data[2], index, ati_remote_tbl[index].code); - if (!dev) - return; /* no mouse device */ - } else - dbginfo(&ati_remote->interface->dev, - "channel 0x%02x; key data %02x, scancode %02x\n", - remote_num, data[2], scancode); - - if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) { input_event(dev, ati_remote_tbl[index].type, ati_remote_tbl[index].code, @@ -542,15 +572,29 @@ static void ati_remote_input_report(struct urb *urb) if (index < 0) { /* Not a mouse event, hand it to rc-core. */ - - /* - * We don't use the rc-core repeat handling yet as - * it would cause ghost repeats which would be a - * regression for this driver. - */ - rc_keydown_notimeout(ati_remote->rdev, scancode, - data[2]); - rc_keyup(ati_remote->rdev); + int count = 1; + + if (wheel_keycode != KEY_RESERVED) { + /* + * This is a scrollwheel event, send the + * scroll up (0x78) / down (0x70) scancode + * repeatedly as many times as indicated by + * rest of the scancode. + */ + count = (scancode & 0x07) + 1; + scancode &= 0x78; + } + + while (count--) { + /* + * We don't use the rc-core repeat handling yet as + * it would cause ghost repeats which would be a + * regression for this driver. + */ + rc_keydown_notimeout(ati_remote->rdev, scancode, + data[2]); + rc_keyup(ati_remote->rdev); + } return; } @@ -766,6 +810,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de struct usb_device *udev = interface_to_usbdev(interface); struct usb_host_interface *iface_host = interface->cur_altsetting; struct usb_endpoint_descriptor *endpoint_in, *endpoint_out; + struct ati_receiver_type *type = (struct ati_receiver_type *)id->driver_info; struct ati_remote *ati_remote; struct input_dev *input_dev; struct rc_dev *rc_dev; @@ -827,10 +872,15 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de snprintf(ati_remote->mouse_name, sizeof(ati_remote->mouse_name), "%s mouse", ati_remote->rc_name); - if (id->driver_info) - rc_dev->map_name = (const char *)id->driver_info; - else - rc_dev->map_name = RC_MAP_ATI_X10; + rc_dev->map_name = RC_MAP_ATI_X10; /* default map */ + + /* set default keymap according to receiver model */ + if (type) { + if (type->default_keymap) + rc_dev->map_name = type->default_keymap; + else if (type->get_default_keymap) + rc_dev->map_name = type->get_default_keymap(interface); + } ati_remote_rc_init(ati_remote); mutex_init(&ati_remote->open_mutex); diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 95e630998aaf..a82025121345 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -46,9 +46,9 @@ static int ir_raw_event_thread(void *data) while (!kthread_should_stop()) { spin_lock_irq(&raw->lock); - retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); + retval = kfifo_len(&raw->kfifo); - if (!retval) { + if (retval < sizeof(ev)) { set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) @@ -59,11 +59,9 @@ static int ir_raw_event_thread(void *data) continue; } + retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); spin_unlock_irq(&raw->lock); - - BUG_ON(retval != sizeof(ev)); - mutex_lock(&ir_raw_handler_lock); list_for_each_entry(handler, &ir_raw_handler_list, list) handler->decode(raw->dev, ev); diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 49ce2662f56b..38ff6e0e099a 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-lme2510.o \ rc-manli.o \ rc-medion-x10.o \ + rc-medion-x10-digitainer.o \ rc-msi-digivox-ii.o \ rc-msi-digivox-iii.o \ rc-msi-tvanywhere.o \ diff --git a/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c new file mode 100644 index 000000000000..0a5ce84d9fd8 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c @@ -0,0 +1,115 @@ +/* + * Medion X10 RF remote keytable (Digitainer variant) + * + * Copyright (C) 2012 Anssi Hannula <anssi.hannula@iki.fi> + * + * This keymap is for a variant that has a distinctive scrollwheel instead of + * up/down buttons (tested with P/N 40009936 / 20018268), reportedly + * originally shipped with Medion Digitainer but now sold separately simply as + * an "X10" remote. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/module.h> +#include <media/rc-map.h> + +static struct rc_map_table medion_x10_digitainer[] = { + { 0x02, KEY_POWER }, + + { 0x2c, KEY_TV }, + { 0x2d, KEY_VIDEO }, + { 0x04, KEY_DVD }, /* CD/DVD */ + { 0x16, KEY_TEXT }, /* "teletext" icon, i.e. a screen with lines */ + { 0x06, KEY_AUDIO }, + { 0x2e, KEY_RADIO }, + { 0x31, KEY_EPG }, /* a screen with an open book */ + { 0x05, KEY_IMAGES }, /* Photo */ + { 0x2f, KEY_INFO }, + + { 0x78, KEY_UP }, /* scrollwheel up 1 notch */ + /* 0x79..0x7f: 2-8 notches, driver repeats 0x78 entry */ + + { 0x70, KEY_DOWN }, /* scrollwheel down 1 notch */ + /* 0x71..0x77: 2-8 notches, driver repeats 0x70 entry */ + + { 0x19, KEY_MENU }, + { 0x1d, KEY_LEFT }, + { 0x1e, KEY_OK }, /* scrollwheel press */ + { 0x1f, KEY_RIGHT }, + { 0x20, KEY_BACK }, + + { 0x09, KEY_VOLUMEUP }, + { 0x08, KEY_VOLUMEDOWN }, + { 0x00, KEY_MUTE }, + + { 0x1b, KEY_SELECT }, /* also has "U" rotated 90 degrees CCW */ + + { 0x0b, KEY_CHANNELUP }, + { 0x0c, KEY_CHANNELDOWN }, + { 0x1c, KEY_LAST }, + + { 0x32, KEY_RED }, /* also Audio */ + { 0x33, KEY_GREEN }, /* also Subtitle */ + { 0x34, KEY_YELLOW }, /* also Angle */ + { 0x35, KEY_BLUE }, /* also Title */ + + { 0x28, KEY_STOP }, + { 0x29, KEY_PAUSE }, + { 0x25, KEY_PLAY }, + { 0x21, KEY_PREVIOUS }, + { 0x18, KEY_CAMERA }, + { 0x23, KEY_NEXT }, + { 0x24, KEY_REWIND }, + { 0x27, KEY_RECORD }, + { 0x26, KEY_FORWARD }, + + { 0x0d, KEY_1 }, + { 0x0e, KEY_2 }, + { 0x0f, KEY_3 }, + { 0x10, KEY_4 }, + { 0x11, KEY_5 }, + { 0x12, KEY_6 }, + { 0x13, KEY_7 }, + { 0x14, KEY_8 }, + { 0x15, KEY_9 }, + { 0x17, KEY_0 }, +}; + +static struct rc_map_list medion_x10_digitainer_map = { + .map = { + .scan = medion_x10_digitainer, + .size = ARRAY_SIZE(medion_x10_digitainer), + .rc_type = RC_TYPE_OTHER, + .name = RC_MAP_MEDION_X10_DIGITAINER, + } +}; + +static int __init init_rc_map_medion_x10_digitainer(void) +{ + return rc_map_register(&medion_x10_digitainer_map); +} + +static void __exit exit_rc_map_medion_x10_digitainer(void) +{ + rc_map_unregister(&medion_x10_digitainer_map); +} + +module_init(init_rc_map_medion_x10_digitainer) +module_exit(exit_rc_map_medion_x10_digitainer) + +MODULE_DESCRIPTION("Medion X10 RF remote keytable (Digitainer variant)"); +MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index e581b37be789..a9cfb0f4be48 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -663,7 +663,7 @@ static const struct v4l2_queryctrl bttv_ctls[] = { .minimum = 0, .maximum = 65535, .step = 128, - .default_value = 32768, + .default_value = 27648, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_SATURATION, @@ -4394,7 +4394,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, if (!bttv_tvcards[btv->c.type].no_video) { bttv_register_video(btv); bt848_bright(btv,32768); - bt848_contrast(btv,32768); + bt848_contrast(btv, 27648); bt848_hue(btv,32768); bt848_sat(btv,32768); audio_mute(btv, 1); diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index f6f622e123bd..928ef0d0429f 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -49,10 +49,10 @@ config VIDEO_EM28XX_DVB Empiatech em28xx chips. config VIDEO_EM28XX_RC - bool "EM28XX Remote Controller support" + tristate "EM28XX Remote Controller support" depends on RC_CORE depends on VIDEO_EM28XX depends on !(RC_CORE=m && VIDEO_EM28XX=y) - default y + default VIDEO_EM28XX ---help--- Enables Remote Controller support on em28xx driver. diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index 2abdf76c5203..c8b338d4be05 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -1,16 +1,15 @@ em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-y += em28xx-core.o em28xx-vbi.o -em28xx-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-input.o - em28xx-alsa-objs := em28xx-audio.o +em28xx-rc-objs := em28xx-input.o obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o +obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o ccflags-y += -Idrivers/media/video ccflags-y += -Idrivers/media/common/tuners ccflags-y += -Idrivers/media/dvb/dvb-core ccflags-y += -Idrivers/media/dvb/frontends - diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 9fd8cc7dbb23..0ac117c23c47 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -2661,54 +2661,6 @@ static int em28xx_hint_board(struct em28xx *dev) return -1; } -/* ----------------------------------------------------------------------- */ -void em28xx_register_i2c_ir(struct em28xx *dev) -{ - /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ - /* at address 0x18, so if that address is needed for another board in */ - /* the future, please put it after 0x1f. */ - struct i2c_board_info info; - const unsigned short addr_list[] = { - 0x1f, 0x30, 0x47, I2C_CLIENT_END - }; - - if (disable_ir) - return; - - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - - /* detect & configure */ - switch (dev->model) { - case EM2800_BOARD_TERRATEC_CINERGY_200: - case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; - dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "i2c IR (EM28XX Terratec)"; - break; - case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; - dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; - break; - case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; - dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; - break; - case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; - dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; - break; - } - - if (dev->init_data.name) - info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); -} - void em28xx_card_setup(struct em28xx *dev) { /* @@ -2849,13 +2801,6 @@ void em28xx_card_setup(struct em28xx *dev) break; } -#if defined(CONFIG_MODULES) && defined(MODULE) - if (dev->board.has_ir_i2c && !disable_ir) - request_module("ir-kbd-i2c"); -#endif - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); - if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) { em28xx_errdev("\n\n"); em28xx_errdev("The support for this board weren't " @@ -2929,9 +2874,6 @@ void em28xx_card_setup(struct em28xx *dev) } em28xx_tuner_setup(dev); - - if(!disable_ir) - em28xx_ir_init(dev); } @@ -2948,6 +2890,8 @@ static void request_module_async(struct work_struct *work) if (dev->board.has_dvb) request_module("em28xx-dvb"); + if (dev->board.has_ir_i2c && !disable_ir) + request_module("em28xx-rc"); } static void request_modules(struct em28xx *dev) @@ -2972,12 +2916,6 @@ static void flush_request_modules(struct em28xx *dev) */ void em28xx_release_resources(struct em28xx *dev) { - if (dev->sbutton_input_dev) - em28xx_deregister_snapshot_button(dev); - - if (dev->ir) - em28xx_ir_fini(dev); - /*FIXME: I2C IR should be disconnected */ em28xx_release_analog_resources(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 53a9fb91e97e..5717bdee8f1b 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -139,6 +139,7 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg) { return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg); } +EXPORT_SYMBOL_GPL(em28xx_read_reg); /* * em28xx_write_regs_req() @@ -205,6 +206,7 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) return rc; } +EXPORT_SYMBOL_GPL(em28xx_write_regs); /* Write a single register */ int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val) @@ -239,6 +241,7 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, return em28xx_write_regs(dev, reg, &newval, 1); } +EXPORT_SYMBOL_GPL(em28xx_write_reg_bits); /* * em28xx_is_ac97_ready() @@ -666,7 +669,6 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } -EXPORT_SYMBOL_GPL(em28xx_capture_start); int em28xx_vbi_supported(struct em28xx *dev) { @@ -975,7 +977,6 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) else isoc_bufs = &dev->isoc_ctl.analog_bufs; - dev->isoc_ctl.nfields = -1; for (i = 0; i < isoc_bufs->num_bufs; i++) { urb = isoc_bufs->urb[i]; if (urb) { @@ -1008,6 +1009,31 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); /* + * Stop URBs + */ +void em28xx_stop_urbs(struct em28xx *dev) +{ + int i; + struct urb *urb; + struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + + em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); + + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = isoc_bufs->urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + } + } + + em28xx_capture_start(dev, 0); +} +EXPORT_SYMBOL_GPL(em28xx_stop_urbs); + +/* * Allocate URBs */ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 503a8d5b5382..2d73ee2abf7e 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -183,7 +183,7 @@ static int em28xx_stop_streaming(struct em28xx_dvb *dvb) { struct em28xx *dev = dvb->adapter.priv; - em28xx_capture_start(dev, 0); + em28xx_stop_urbs(dev); em28xx_set_mode(dev, EM28XX_SUSPEND); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index a88e169dba23..185db65b766e 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -553,9 +553,6 @@ int em28xx_i2c_register(struct em28xx *dev) if (i2c_scan) em28xx_do_i2c_scan(dev); - /* Instantiate the IR receiver device, if present */ - em28xx_register_i2c_ir(dev); - return 0; } diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 2630b265b0e8..fce5f7680c99 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -80,7 +80,7 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char b; @@ -108,7 +108,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[2]; u16 code; @@ -157,7 +157,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, +static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[3]; @@ -179,7 +179,8 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } -int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) { unsigned char subaddr, keydetect, key; @@ -387,7 +388,138 @@ int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) return rc; } -int em28xx_ir_init(struct em28xx *dev) +static void em28xx_register_i2c_ir(struct em28xx *dev) +{ + /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ + /* at address 0x18, so if that address is needed for another board in */ + /* the future, please put it after 0x1f. */ + struct i2c_board_info info; + const unsigned short addr_list[] = { + 0x1f, 0x30, 0x47, I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + + /* detect & configure */ + switch (dev->model) { + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; + dev->init_data.get_key = em28xx_get_key_terratec; + dev->init_data.name = "i2c IR (EM28XX Terratec)"; + break; + case EM2820_BOARD_PINNACLE_USB_2: + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; + dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; + dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + break; + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; + dev->init_data.get_key = em28xx_get_key_em_haup; + dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + break; + case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: + dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; + dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; + dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; + break; + } + + if (dev->init_data.name) + info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); +} + +/********************************************************** + Handle Webcam snapshot button + **********************************************************/ + +static void em28xx_query_sbutton(struct work_struct *work) +{ + /* Poll the register and see if the button is depressed */ + struct em28xx *dev = + container_of(work, struct em28xx, sbutton_query_work.work); + int ret; + + ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); + + if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { + u8 cleared; + /* Button is depressed, clear the register */ + cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; + em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); + + /* Not emulate the keypress */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 1); + /* Now unpress the key */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 0); + } + + /* Schedule next poll */ + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); +} + +static void em28xx_register_snapshot_button(struct em28xx *dev) +{ + struct input_dev *input_dev; + int err; + + em28xx_info("Registering snapshot button...\n"); + input_dev = input_allocate_device(); + if (!input_dev) { + em28xx_errdev("input_allocate_device failed\n"); + return; + } + + usb_make_path(dev->udev, dev->snapshot_button_path, + sizeof(dev->snapshot_button_path)); + strlcat(dev->snapshot_button_path, "/sbutton", + sizeof(dev->snapshot_button_path)); + INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); + + input_dev->name = "em28xx snapshot button"; + input_dev->phys = dev->snapshot_button_path; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); + input_dev->keycodesize = 0; + input_dev->keycodemax = 0; + input_dev->id.bustype = BUS_USB; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + input_dev->id.version = 1; + input_dev->dev.parent = &dev->udev->dev; + + err = input_register_device(input_dev); + if (err) { + em28xx_errdev("input_register_device failed\n"); + input_free_device(input_dev); + return; + } + + dev->sbutton_input_dev = input_dev; + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + return; + +} + +static void em28xx_deregister_snapshot_button(struct em28xx *dev) +{ + if (dev->sbutton_input_dev != NULL) { + em28xx_info("Deregistering snapshot button\n"); + cancel_delayed_work_sync(&dev->sbutton_query_work); + input_unregister_device(dev->sbutton_input_dev); + dev->sbutton_input_dev = NULL; + } + return; +} + +static int em28xx_ir_init(struct em28xx *dev) { struct em28xx_IR *ir; struct rc_dev *rc; @@ -448,6 +580,15 @@ int em28xx_ir_init(struct em28xx *dev) if (err) goto err_out_stop; + em28xx_register_i2c_ir(dev); + +#if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->board.has_ir_i2c) + request_module("ir-kbd-i2c"); +#endif + if (dev->board.has_snapshot_button) + em28xx_register_snapshot_button(dev); + return 0; err_out_stop: @@ -458,10 +599,12 @@ int em28xx_ir_init(struct em28xx *dev) return err; } -int em28xx_ir_fini(struct em28xx *dev) +static int em28xx_ir_fini(struct em28xx *dev) { struct em28xx_IR *ir = dev->ir; + em28xx_deregister_snapshot_button(dev); + /* skip detach on non attached boards */ if (!ir) return 0; @@ -475,89 +618,26 @@ int em28xx_ir_fini(struct em28xx *dev) return 0; } -/********************************************************** - Handle Webcam snapshot button - **********************************************************/ +static struct em28xx_ops rc_ops = { + .id = EM28XX_RC, + .name = "Em28xx Input Extension", + .init = em28xx_ir_init, + .fini = em28xx_ir_fini, +}; -static void em28xx_query_sbutton(struct work_struct *work) +static int __init em28xx_rc_register(void) { - /* Poll the register and see if the button is depressed */ - struct em28xx *dev = - container_of(work, struct em28xx, sbutton_query_work.work); - int ret; - - ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); - - if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { - u8 cleared; - /* Button is depressed, clear the register */ - cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; - em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); - - /* Not emulate the keypress */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 1); - /* Now unpress the key */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 0); - } - - /* Schedule next poll */ - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + return em28xx_register_extension(&rc_ops); } -void em28xx_register_snapshot_button(struct em28xx *dev) +static void __exit em28xx_rc_unregister(void) { - struct input_dev *input_dev; - int err; - - em28xx_info("Registering snapshot button...\n"); - input_dev = input_allocate_device(); - if (!input_dev) { - em28xx_errdev("input_allocate_device failed\n"); - return; - } - - usb_make_path(dev->udev, dev->snapshot_button_path, - sizeof(dev->snapshot_button_path)); - strlcat(dev->snapshot_button_path, "/sbutton", - sizeof(dev->snapshot_button_path)); - INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); - - input_dev->name = "em28xx snapshot button"; - input_dev->phys = dev->snapshot_button_path; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); - input_dev->keycodesize = 0; - input_dev->keycodemax = 0; - input_dev->id.bustype = BUS_USB; - input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); - input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); - input_dev->id.version = 1; - input_dev->dev.parent = &dev->udev->dev; - - err = input_register_device(input_dev); - if (err) { - em28xx_errdev("input_register_device failed\n"); - input_free_device(input_dev); - return; - } - - dev->sbutton_input_dev = input_dev; - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); - return; - + em28xx_unregister_extension(&rc_ops); } -void em28xx_deregister_snapshot_button(struct em28xx *dev) -{ - if (dev->sbutton_input_dev != NULL) { - em28xx_info("Deregistering snapshot button\n"); - cancel_delayed_work_sync(&dev->sbutton_query_work); - input_unregister_device(dev->sbutton_input_dev); - dev->sbutton_input_dev = NULL; - } - return; -} +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_DESCRIPTION("Em28xx Input driver"); + +module_init(em28xx_rc_register); +module_exit(em28xx_rc_unregister); diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 324b695c0724..bcc41603c193 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1305,9 +1305,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) if (0 == INPUT(i)->type) return -EINVAL; - dev->ctl_input = i; - - video_mux(dev, dev->ctl_input); + video_mux(dev, i); return 0; } @@ -2518,7 +2516,6 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->norm = em28xx_video_template.current_norm; v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); dev->interlaced = EM28XX_INTERLACED_DEFAULT; - dev->ctl_input = 0; /* Analog specific initialization */ dev->format = &format[0]; @@ -2532,7 +2529,7 @@ int em28xx_register_analog_devices(struct em28xx *dev) em28xx_set_video_format(dev, format[0].fourcc, maxw, norm_maxh(dev)); - video_mux(dev, dev->ctl_input); + video_mux(dev, 0); /* Audio defaults */ dev->mute = 1; diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 2868b19f8b54..9a2bd5c40163 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -226,24 +226,10 @@ struct em28xx_usb_isoc_ctl { /* isoc transfer buffers for digital mode */ struct em28xx_usb_isoc_bufs digital_bufs; - /* Last buffer command and region */ - u8 cmd; - int pos, size, pktsize; - - /* Last field: ODD or EVEN? */ - int field; - - /* Stores incomplete commands */ - u32 tmp_buf; - int tmp_buf_len; - /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; struct em28xx_buffer *vbi_buf; - /* Stores the number of received fields */ - int nfields; - /* isoc urb callback */ int (*isoc_copy) (struct em28xx *dev, struct urb *urb); @@ -467,6 +453,7 @@ enum em28xx_dev_state { /* em28xx extensions */ #define EM28XX_AUDIO 0x10 #define EM28XX_DVB 0x20 +#define EM28XX_RC 0x30 /* em28xx resource types (used for res_get/res_lock etc */ #define EM28XX_RESOURCE_VIDEO 0x01 @@ -695,6 +682,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, int max_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); +void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); @@ -715,40 +703,9 @@ extern void em28xx_card_setup(struct em28xx *dev); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; -void em28xx_register_i2c_ir(struct em28xx *dev); int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); -/* Provided by em28xx-input.c */ - -#ifdef CONFIG_VIDEO_EM28XX_RC - -int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw); -int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw); -void em28xx_register_snapshot_button(struct em28xx *dev); -void em28xx_deregister_snapshot_button(struct em28xx *dev); - -int em28xx_ir_init(struct em28xx *dev); -int em28xx_ir_fini(struct em28xx *dev); - -#else - -#define em28xx_get_key_terratec NULL -#define em28xx_get_key_em_haup NULL -#define em28xx_get_key_pinnacle_usb_grey NULL -#define em28xx_get_key_winfast_usbii_deluxe NULL - -static inline void em28xx_register_snapshot_button(struct em28xx *dev) {} -static inline void em28xx_deregister_snapshot_button(struct em28xx *dev) {} -static inline int em28xx_ir_init(struct em28xx *dev) { return 0; } -static inline int em28xx_ir_fini(struct em28xx *dev) { return 0; } - -#endif - /* Provided by em28xx-vbi.c */ extern struct videobuf_queue_ops em28xx_vbi_qops; diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index c9663e885b9f..9ff69b5a87e2 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -746,8 +746,9 @@ unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) return res; } -unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; @@ -755,7 +756,8 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) unsigned res = 0; /* Start a capture if there is none */ - if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags) && + (req_events & (POLLIN | POLLRDNORM))) { int rc; rc = ivtv_start_capture(id); diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c index ba89a7401c8c..55ac1735e85b 100644 --- a/drivers/media/video/mx2_emmaprp.c +++ b/drivers/media/video/mx2_emmaprp.c @@ -755,7 +755,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_MMAP; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &emmaprp_qops; @@ -767,7 +767,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_MMAP; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &emmaprp_qops; diff --git a/drivers/media/video/omap3isp/ispqueue.h b/drivers/media/video/omap3isp/ispqueue.h index 92c5a12157d5..908dfd712e8e 100644 --- a/drivers/media/video/omap3isp/ispqueue.h +++ b/drivers/media/video/omap3isp/ispqueue.h @@ -90,7 +90,7 @@ struct isp_video_buffer { void *vaddr; /* For userspace buffers. */ - unsigned long vm_flags; + vm_flags_t vm_flags; unsigned long offset; unsigned int npages; struct page **pages; diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c index 80e07794ac8e..0bc93313d37a 100644 --- a/drivers/media/video/ov5642.c +++ b/drivers/media/video/ov5642.c @@ -1025,8 +1025,6 @@ static int ov5642_probe(struct i2c_client *client, priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; - priv->crop_rect.width = OV5642_DEFAULT_WIDTH; - priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; priv->total_height = BLANKING_MIN_HEIGHT; diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 122fbd0081eb..f3370a87cbc0 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -625,10 +625,19 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, static unsigned int pwc_video_poll(struct file *file, poll_table *wait) { struct pwc_device *pdev = video_drvdata(file); + unsigned long req_events = poll_requested_events(wait); if (!pdev->udev) return POLL_ERR; + if ((req_events & (POLLIN | POLLRDNORM)) && + pdev->vb_queue.num_buffers == 0 && + !pdev->iso_init) { + /* This poll will start a read stream, check capt_file */ + if (pwc_test_n_set_capt_file(pdev, file)) + return POLL_ERR; + } + return vb2_poll(&pdev->vb_queue, file, wait); } diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 2834e3e65b39..c1ba1a060c93 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -1166,4 +1166,6 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_enum_frameintervals = pwc_enum_frameintervals, .vidioc_g_parm = pwc_g_parm, .vidioc_s_parm = pwc_s_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 4894cbb1c547..37845def41c5 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -1826,8 +1826,7 @@ static void s2255_destroy(struct s2255_dev *dev) usb_free_urb(dev->fw_data->fw_urb); dev->fw_data->fw_urb = NULL; } - if (dev->fw_data->fw) - release_firmware(dev->fw_data->fw); + release_firmware(dev->fw_data->fw); kfree(dev->fw_data->pfw_data); kfree(dev->fw_data); /* reset the DSP so firmware can be reloaded next time */ diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index b06efd208328..dc18ba510986 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -83,7 +83,9 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM); - if (!suspend) + if (suspend) + fimc->state |= (1 << ST_CAPT_SUSPENDED); + else fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED); /* Release unused buffers */ @@ -99,7 +101,6 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) else vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } - set_bit(ST_CAPT_SUSPENDED, &fimc->state); fimc_hw_reset(fimc); cap->buf_index = 0; @@ -146,21 +147,22 @@ int fimc_capture_config_update(struct fimc_ctx *ctx) if (!test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) return 0; - spin_lock(&ctx->slock); fimc_hw_set_camera_offset(fimc, &ctx->s_frame); + ret = fimc_set_scaler_info(ctx); - if (ret == 0) { - fimc_hw_set_prescaler(ctx); - fimc_hw_set_mainscaler(ctx); - fimc_hw_set_target_format(ctx); - fimc_hw_set_rotation(ctx); - fimc_prepare_dma_offset(ctx, &ctx->d_frame); - fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) - fimc_hw_set_rgb_alpha(ctx); - clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); - } - spin_unlock(&ctx->slock); + if (ret) + return ret; + + fimc_hw_set_prescaler(ctx); + fimc_hw_set_mainscaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_prepare_dma_offset(ctx, &ctx->d_frame); + fimc_hw_set_out_dma(ctx); + if (fimc->variant->has_alpha) + fimc_hw_set_rgb_alpha(ctx); + + clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); return ret; } @@ -1524,7 +1526,6 @@ int fimc_register_capture_device(struct fimc_dev *fimc, INIT_LIST_HEAD(&vid_cap->pending_buf_q); INIT_LIST_HEAD(&vid_cap->active_buf_q); - spin_lock_init(&ctx->slock); vid_cap->ctx = ctx; q = &fimc->vid_cap.vbq; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index e184e650022a..7b90a897beeb 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -320,7 +320,7 @@ static int fimc_m2m_shutdown(struct fimc_ctx *ctx) if (!fimc_m2m_pending(fimc)) return 0; - fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx); + fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); ret = wait_event_timeout(fimc->irq_queue, !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), @@ -430,14 +430,12 @@ static irqreturn_t fimc_irq_handler(int irq, void *priv) spin_unlock(&fimc->slock); fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); - spin_lock(&ctx->slock); if (ctx->state & FIMC_CTX_SHUT) { ctx->state &= ~FIMC_CTX_SHUT; wake_up(&fimc->irq_queue); } - spin_unlock(&ctx->slock); + return IRQ_HANDLED; } - return IRQ_HANDLED; } else if (test_bit(ST_CAPT_PEND, &fimc->state)) { fimc_capture_irq_handler(fimc, !test_bit(ST_CAPT_JPEG, &fimc->state)); @@ -584,55 +582,11 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); } -/** - * fimc_prepare_config - check dimensions, operation and color mode - * and pre-calculate offset and the scaling coefficients. - * - * @ctx: hardware context information - * @flags: flags indicating which parameters to check/update - * - * Return: 0 if dimensions are valid or non zero otherwise. - */ -int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) -{ - struct fimc_frame *s_frame, *d_frame; - struct vb2_buffer *vb = NULL; - int ret = 0; - - s_frame = &ctx->s_frame; - d_frame = &ctx->d_frame; - - if (flags & FIMC_PARAMS) { - /* Prepare the DMA offset ratios for scaler. */ - fimc_prepare_dma_offset(ctx, &ctx->s_frame); - fimc_prepare_dma_offset(ctx, &ctx->d_frame); - - if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) || - s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) { - err("out of scaler range"); - return -EINVAL; - } - fimc_set_yuv_order(ctx); - } - - if (flags & FIMC_SRC_ADDR) { - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr); - if (ret) - return ret; - } - - if (flags & FIMC_DST_ADDR) { - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, d_frame, &d_frame->paddr); - } - - return ret; -} - static void fimc_dma_run(void *priv) { + struct vb2_buffer *vb = NULL; struct fimc_ctx *ctx = priv; + struct fimc_frame *sf, *df; struct fimc_dev *fimc; unsigned long flags; u32 ret; @@ -643,10 +597,22 @@ static void fimc_dma_run(void *priv) fimc = ctx->fimc_dev; spin_lock_irqsave(&fimc->slock, flags); set_bit(ST_M2M_PEND, &fimc->state); + sf = &ctx->s_frame; + df = &ctx->d_frame; + + if (ctx->state & FIMC_PARAMS) { + /* Prepare the DMA offsets for scaler */ + fimc_prepare_dma_offset(ctx, sf); + fimc_prepare_dma_offset(ctx, df); + } + + vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); + if (ret) + goto dma_unlock; - spin_lock(&ctx->slock); - ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR); - ret = fimc_prepare_config(ctx, ctx->state); + vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); if (ret) goto dma_unlock; @@ -655,32 +621,26 @@ static void fimc_dma_run(void *priv) ctx->state |= FIMC_PARAMS; fimc->m2m.ctx = ctx; } - fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr); if (ctx->state & FIMC_PARAMS) { + fimc_set_yuv_order(ctx); fimc_hw_set_input_path(ctx); fimc_hw_set_in_dma(ctx); ret = fimc_set_scaler_info(ctx); - if (ret) { - spin_unlock(&fimc->slock); + if (ret) goto dma_unlock; - } fimc_hw_set_prescaler(ctx); fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx, false); - } - - fimc_hw_set_output_path(ctx); - if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS)) - fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr, -1); - - if (ctx->state & FIMC_PARAMS) { fimc_hw_set_out_dma(ctx); if (fimc->variant->has_alpha) fimc_hw_set_rgb_alpha(ctx); + fimc_hw_set_output_path(ctx); } + fimc_hw_set_input_addr(fimc, &sf->paddr); + fimc_hw_set_output_addr(fimc, &df->paddr, -1); fimc_activate_capture(ctx); @@ -688,7 +648,6 @@ static void fimc_dma_run(void *priv) FIMC_SRC_FMT | FIMC_DST_FMT); fimc_hw_activate_input_dma(fimc, true); dma_unlock: - spin_unlock(&ctx->slock); spin_unlock_irqrestore(&fimc->slock, flags); } @@ -827,9 +786,9 @@ static int fimc_s_ctrl(struct v4l2_ctrl *ctrl) unsigned long flags; int ret; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ret = __fimc_s_ctrl(ctx, ctrl); - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); return ret; } @@ -1174,9 +1133,9 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, ctx->scaler.enabled = 1; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); + fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); else - fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); + fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); @@ -1363,7 +1322,7 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) f->width = cr->c.width; f->height = cr->c.height; - fimc_ctx_state_lock_set(FIMC_PARAMS, ctx); + fimc_ctx_state_set(FIMC_PARAMS, ctx); return 0; } @@ -1467,7 +1426,6 @@ static int fimc_m2m_open(struct file *file) ctx->flags = 0; ctx->in_path = FIMC_DMA; ctx->out_path = FIMC_DMA; - spin_lock_init(&ctx->slock); ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { @@ -1601,7 +1559,7 @@ void fimc_unregister_m2m_device(struct fimc_dev *fimc) static void fimc_clk_put(struct fimc_dev *fimc) { int i; - for (i = 0; i < fimc->num_clocks; i++) { + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { if (IS_ERR_OR_NULL(fimc->clock[i])) continue; clk_unprepare(fimc->clock[i]); @@ -1614,7 +1572,7 @@ static int fimc_clk_get(struct fimc_dev *fimc) { int i, ret; - for (i = 0; i < fimc->num_clocks; i++) { + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); if (IS_ERR(fimc->clock[i])) goto err; @@ -1714,9 +1672,7 @@ static int fimc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to get IRQ resource\n"); return -ENXIO; } - fimc->irq = res->start; - fimc->num_clocks = MAX_FIMC_CLOCKS; ret = fimc_clk_get(fimc); if (ret) return ret; @@ -1725,7 +1681,7 @@ static int fimc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fimc); - ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler, + ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, 0, pdev->name, fimc); if (ret) { dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); @@ -2036,7 +1992,7 @@ static struct platform_driver fimc_driver = { int __init fimc_register_driver(void) { - return platform_driver_probe(&fimc_driver, fimc_probe); + return platform_driver_register(&fimc_driver); } void __exit fimc_unregister_driver(void) diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index a18291e648e2..193e8f603949 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -119,8 +119,6 @@ enum fimc_color_fmt { /* The hardware context state. */ #define FIMC_PARAMS (1 << 0) -#define FIMC_SRC_ADDR (1 << 1) -#define FIMC_DST_ADDR (1 << 2) #define FIMC_SRC_FMT (1 << 3) #define FIMC_DST_FMT (1 << 4) #define FIMC_DST_CROP (1 << 5) @@ -431,10 +429,8 @@ struct fimc_ctx; * @pdata: pointer to the device platform data * @variant: the IP variant information * @id: FIMC device index (0..FIMC_MAX_DEVS) - * @num_clocks: the number of clocks managed by this device instance * @clock: clocks required for FIMC operation * @regs: the mapped hardware registers - * @irq: FIMC interrupt number * @irq_queue: interrupt handler waitqueue * @v4l2_dev: root v4l2_device * @m2m: memory-to-memory V4L2 device information @@ -450,10 +446,8 @@ struct fimc_dev { struct s5p_platform_fimc *pdata; struct samsung_fimc_variant *variant; u16 id; - u16 num_clocks; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; - int irq; wait_queue_head_t irq_queue; struct v4l2_device *v4l2_dev; struct fimc_m2m_device m2m; @@ -465,7 +459,6 @@ struct fimc_dev { /** * fimc_ctx - the device context data - * @slock: spinlock protecting this data structure * @s_frame: source frame properties * @d_frame: destination frame properties * @out_order_1p: output 1-plane YCBCR order @@ -492,7 +485,6 @@ struct fimc_dev { * @ctrls_rdy: true if the control handler is initialized */ struct fimc_ctx { - spinlock_t slock; struct fimc_frame s_frame; struct fimc_frame d_frame; u32 out_order_1p; @@ -560,13 +552,13 @@ static inline bool fimc_capture_active(struct fimc_dev *fimc) return ret; } -static inline void fimc_ctx_state_lock_set(u32 state, struct fimc_ctx *ctx) +static inline void fimc_ctx_state_set(u32 state, struct fimc_ctx *ctx) { unsigned long flags; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ctx->state |= state; - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); } static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) @@ -574,9 +566,9 @@ static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) unsigned long flags; bool ret; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ret = (ctx->state & mask) == mask; - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); return ret; } diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 62ed37e40149..75296a625a9d 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -214,14 +214,20 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, return NULL; adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num); - if (!adapter) - return NULL; + if (!adapter) { + v4l2_warn(&fmd->v4l2_dev, + "Failed to get I2C adapter %d, deferring probe\n", + s_info->pdata->i2c_bus_num); + return ERR_PTR(-EPROBE_DEFER); + } sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, s_info->pdata->board_info, NULL); if (IS_ERR_OR_NULL(sd)) { i2c_put_adapter(adapter); - v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n"); - return NULL; + v4l2_warn(&fmd->v4l2_dev, + "Failed to acquire subdev %s, deferring probe\n", + s_info->pdata->board_info->type); + return ERR_PTR(-EPROBE_DEFER); } v4l2_set_subdev_hostdata(sd, s_info); sd->grp_id = SENSOR_GROUP_ID; @@ -269,13 +275,22 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) fmd->num_sensors = num_clients; for (i = 0; i < num_clients; i++) { + struct v4l2_subdev *sd; + fmd->sensor[i].pdata = &pdata->isp_info[i]; ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); if (ret) break; - fmd->sensor[i].subdev = - fimc_md_register_sensor(fmd, &fmd->sensor[i]); + sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); + + if (!IS_ERR(sd)) { + fmd->sensor[i].subdev = sd; + } else { + fmd->sensor[i].subdev = NULL; + ret = PTR_ERR(sd); + break; + } if (ret) break; } @@ -336,22 +351,45 @@ static int csis_register_callback(struct device *dev, void *p) */ static int fimc_md_register_platform_entities(struct fimc_md *fmd) { + struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; struct device_driver *driver; - int ret; + int ret, i; driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); - if (!driver) - return -ENODEV; + if (!driver) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + FIMC_MODULE_NAME); + return -EPROBE_DEFER; + } + ret = driver_for_each_device(driver, NULL, fmd, fimc_register_callback); if (ret) return ret; + /* + * Check if there is any sensor on the MIPI-CSI2 bus and + * if not skip the s5p-csis module loading. + */ + for (i = 0; i < pdata->num_clients; i++) { + if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) { + ret = 1; + break; + } + } + if (!ret) + return 0; driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); - if (driver) - ret = driver_for_each_device(driver, NULL, fmd, - csis_register_callback); - return ret; + if (!driver || !try_module_get(driver->owner)) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + CSIS_DRIVER_NAME); + return -EPROBE_DEFER; + } + + return driver_for_each_device(driver, NULL, fmd, + csis_register_callback); } static void fimc_md_unregister_entities(struct fimc_md *fmd) @@ -369,6 +407,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) if (fmd->csis[i].sd == NULL) continue; v4l2_device_unregister_subdev(fmd->csis[i].sd); + module_put(fmd->csis[i].sd->owner); fmd->csis[i].sd = NULL; } for (i = 0; i < fmd->num_sensors; i++) { @@ -744,7 +783,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, fimc_md_sysfs_show, fimc_md_sysfs_store); -static int __devinit fimc_md_probe(struct platform_device *pdev) +static int fimc_md_probe(struct platform_device *pdev) { struct v4l2_device *v4l2_dev; struct fimc_md *fmd; @@ -841,10 +880,12 @@ static struct platform_driver fimc_md_driver = { int __init fimc_md_init(void) { int ret; + request_module("s5p-csis"); ret = fimc_register_driver(); if (ret) return ret; + return platform_driver_register(&fimc_md_driver); } void __exit fimc_md_exit(void) diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 15466d0529c1..ff11f10fea0b 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -674,6 +674,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, { u32 cfg, tmp; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + u32 csis_data_alignment = 32; cfg = readl(fimc->regs + S5P_CIGCTRL); @@ -703,7 +704,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, vid_cap->mf.code); return -EINVAL; } - tmp |= (cam->csi_data_align == 32) << 8; + tmp |= (csis_data_alignment == 32) << 8; writel(tmp, fimc->regs + S5P_CSIIMGFMT); diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c index f44f690397f7..2f73d9e3d0b7 100644 --- a/drivers/media/video/s5p-fimc/mipi-csis.c +++ b/drivers/media/video/s5p-fimc/mipi-csis.c @@ -127,20 +127,24 @@ struct csis_state { * multiple of 2^pix_width_alignment * @code: corresponding media bus code * @fmt_reg: S5PCSIS_CONFIG register value + * @data_alignment: MIPI-CSI data alignment in bits */ struct csis_pix_format { unsigned int pix_width_alignment; enum v4l2_mbus_pixelcode code; u32 fmt_reg; + u8 data_alignment; }; static const struct csis_pix_format s5pcsis_formats[] = { { .code = V4L2_MBUS_FMT_VYUY8_2X8, .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT, + .data_alignment = 32, }, { .code = V4L2_MBUS_FMT_JPEG_1X8, .fmt_reg = S5PCSIS_CFG_FMT_USER(1), + .data_alignment = 32, }, }; @@ -239,7 +243,7 @@ static void s5pcsis_set_params(struct csis_state *state) s5pcsis_set_hsync_settle(state, pdata->hs_settle); val = s5pcsis_read(state, S5PCSIS_CTRL); - if (pdata->alignment == 32) + if (state->csis_fmt->data_alignment == 32) val |= S5PCSIS_CTRL_ALIGN_32BIT; else /* 24-bits */ val &= ~S5PCSIS_CTRL_ALIGN_32BIT; @@ -711,19 +715,8 @@ static struct platform_driver s5pcsis_driver = { }, }; -static int __init s5pcsis_init(void) -{ - return platform_driver_probe(&s5pcsis_driver, s5pcsis_probe); -} - -static void __exit s5pcsis_exit(void) -{ - platform_driver_unregister(&s5pcsis_driver); -} - -module_init(s5pcsis_init); -module_exit(s5pcsis_exit); +module_platform_driver(s5pcsis_driver); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); -MODULE_DESCRIPTION("S5P/EXYNOS4 MIPI CSI receiver driver"); +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c index a2c0c25ad130..edca06592883 100644 --- a/drivers/media/video/s5p-tv/mixer_drv.c +++ b/drivers/media/video/s5p-tv/mixer_drv.c @@ -461,7 +461,7 @@ static struct platform_driver mxr_driver __refdata = { static int __init mxr_init(void) { int i, ret; - static const char banner[] __initdata = KERN_INFO + static const char banner[] __initconst = KERN_INFO "Samsung TV Mixer driver, " "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; printk(banner); diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index ff2cdddf9bc6..111bfff1640d 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -1105,8 +1105,6 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, if (get_user(p, &up->menu_info)) return -EFAULT; umenus = compat_ptr(p); - if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus))) - return -EFAULT; if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) return -EFAULT; diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 18015c0a8d31..1a71aa5fd458 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -852,7 +852,8 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change ev->u.ctrl.value64 = ctrl->cur.val64; ev->u.ctrl.minimum = ctrl->minimum; ev->u.ctrl.maximum = ctrl->maximum; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) ev->u.ctrl.step = 1; else ev->u.ctrl.step = ctrl->step; @@ -1083,10 +1084,13 @@ static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval) return 0; case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: if (val < ctrl->minimum || val > ctrl->maximum) return -ERANGE; - if (ctrl->qmenu[val][0] == '\0' || - (ctrl->menu_skip_mask & (1 << val))) + if (ctrl->menu_skip_mask & (1 << val)) + return -EINVAL; + if (ctrl->type == V4L2_CTRL_TYPE_MENU && + ctrl->qmenu[val][0] == '\0') return -EINVAL; return 0; @@ -1114,6 +1118,7 @@ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: @@ -1343,7 +1348,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, s32 min, s32 max, u32 step, s32 def, - u32 flags, const char * const *qmenu, void *priv) + u32 flags, const char * const *qmenu, + const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; unsigned sz_extra = 0; @@ -1356,6 +1362,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, (type == V4L2_CTRL_TYPE_INTEGER && step == 0) || (type == V4L2_CTRL_TYPE_BITMASK && max == 0) || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || + (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) || (type == V4L2_CTRL_TYPE_STRING && max == 0)) { handler_set_err(hdl, -ERANGE); return NULL; @@ -1366,6 +1373,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } if ((type == V4L2_CTRL_TYPE_INTEGER || type == V4L2_CTRL_TYPE_MENU || + type == V4L2_CTRL_TYPE_INTEGER_MENU || type == V4L2_CTRL_TYPE_BOOLEAN) && (def < min || def > max)) { handler_set_err(hdl, -ERANGE); @@ -1400,7 +1408,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; - ctrl->qmenu = qmenu; + if (type == V4L2_CTRL_TYPE_MENU) + ctrl->qmenu = qmenu; + else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) + ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; ctrl->cur.val = ctrl->val = ctrl->default_value = def; @@ -1427,6 +1438,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl; const char *name = cfg->name; const char * const *qmenu = cfg->qmenu; + const s64 *qmenu_int = cfg->qmenu_int; enum v4l2_ctrl_type type = cfg->type; u32 flags = cfg->flags; s32 min = cfg->min; @@ -1438,18 +1450,24 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, &def, &flags); - is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU); + is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU || + cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU); if (is_menu) WARN_ON(step); else WARN_ON(cfg->menu_skip_mask); - if (is_menu && qmenu == NULL) + if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) qmenu = v4l2_ctrl_get_menu(cfg->id); + else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU && + qmenu_int == NULL) { + handler_set_err(hdl, -EINVAL); + return NULL; + } ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, - def, flags, qmenu, priv); + def, flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; @@ -1466,12 +1484,13 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type == V4L2_CTRL_TYPE_MENU) { + if (type == V4L2_CTRL_TYPE_MENU + || type == V4L2_CTRL_TYPE_INTEGER_MENU) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - min, max, step, def, flags, NULL, NULL); + min, max, step, def, flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1493,7 +1512,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, NULL); + 0, max, mask, def, flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -1659,6 +1678,9 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]); + break; case V4L2_CTRL_TYPE_BITMASK: printk(KERN_CONT "0x%08x", ctrl->cur.val); break; @@ -1795,7 +1817,8 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) qc->step = 1; else qc->step = ctrl->step; @@ -1825,16 +1848,33 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm) qm->reserved = 0; /* Sanity checks */ - if (ctrl->qmenu == NULL || - i < ctrl->minimum || i > ctrl->maximum) + switch (ctrl->type) { + case V4L2_CTRL_TYPE_MENU: + if (ctrl->qmenu == NULL) + return -EINVAL; + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (ctrl->qmenu_int == NULL) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (i < ctrl->minimum || i > ctrl->maximum) return -EINVAL; + /* Use mask to see if this menu item should be skipped */ if (ctrl->menu_skip_mask & (1 << i)) return -EINVAL; /* Empty menu items should also be skipped */ - if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') - return -EINVAL; - strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + if (ctrl->type == V4L2_CTRL_TYPE_MENU) { + if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') + return -EINVAL; + strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + } else { + qm->value = ctrl->qmenu_int[i]; + } return 0; } EXPORT_SYMBOL(v4l2_querymenu); @@ -1996,7 +2036,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs return class_check(hdl, cs->ctrl_class); if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + helpers = kmalloc_array(cs->count, sizeof(helper[0]), + GFP_KERNEL); if (helpers == NULL) return -ENOMEM; } @@ -2218,7 +2259,8 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, return class_check(hdl, cs->ctrl_class); if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + helpers = kmalloc_array(cs->count, sizeof(helper[0]), + GFP_KERNEL); if (!helpers) return -ENOMEM; } diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 6fe88e965a8c..268d80584101 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -35,14 +35,9 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - /* Allocate try format and crop in the same memory block */ - fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) - * sd->entity.num_pads, GFP_KERNEL); - if (fh->try_fmt == NULL) + fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL); + if (fh->pad == NULL) return -ENOMEM; - - fh->try_crop = (struct v4l2_rect *) - (fh->try_fmt + sd->entity.num_pads); #endif return 0; } @@ -50,9 +45,8 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) static void subdev_fh_free(struct v4l2_subdev_fh *fh) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - kfree(fh->try_fmt); - fh->try_fmt = NULL; - fh->try_crop = NULL; + kfree(fh->pad); + fh->pad = NULL; #endif } @@ -234,6 +228,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_CROP: { struct v4l2_subdev_crop *crop = arg; + struct v4l2_subdev_selection sel; + int rval; if (crop->which != V4L2_SUBDEV_FORMAT_TRY && crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) @@ -242,11 +238,27 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (crop->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); + rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); + if (rval != -ENOIOCTLCMD) + return rval; + + memset(&sel, 0, sizeof(sel)); + sel.which = crop->which; + sel.pad = crop->pad; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + + rval = v4l2_subdev_call( + sd, pad, get_selection, subdev_fh, &sel); + + crop->rect = sel.r; + + return rval; } case VIDIOC_SUBDEV_S_CROP: { struct v4l2_subdev_crop *crop = arg; + struct v4l2_subdev_selection sel; + int rval; if (crop->which != V4L2_SUBDEV_FORMAT_TRY && crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) @@ -255,7 +267,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (crop->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); + rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); + if (rval != -ENOIOCTLCMD) + return rval; + + memset(&sel, 0, sizeof(sel)); + sel.which = crop->which; + sel.pad = crop->pad; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.r = crop->rect; + + rval = v4l2_subdev_call( + sd, pad, set_selection, subdev_fh, &sel); + + crop->rect = sel.r; + + return rval; } case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { @@ -293,6 +320,34 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, fie); } + + case VIDIOC_SUBDEV_G_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, get_selection, subdev_fh, sel); + } + + case VIDIOC_SUBDEV_S_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, set_selection, subdev_fh, sel); + } #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index de4fa4eb8844..ffdf59cfe405 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -1129,6 +1129,7 @@ unsigned int videobuf_poll_stream(struct file *file, struct videobuf_queue *q, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct videobuf_buffer *buf = NULL; unsigned int rc = 0; @@ -1137,7 +1138,7 @@ unsigned int videobuf_poll_stream(struct file *file, if (!list_empty(&q->stream)) buf = list_entry(q->stream.next, struct videobuf_buffer, stream); - } else { + } else if (req_events & (POLLIN | POLLRDNORM)) { if (!q->reading) __videobuf_read_start(q); if (!q->reading) { diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 2e8f1df775b6..3786d88183eb 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -19,6 +19,9 @@ #include <linux/slab.h> #include <linux/sched.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> #include <media/videobuf2-core.h> static int debug; @@ -1642,32 +1645,46 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q); * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor * will be reported as available for writing. * + * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any + * pending events. + * * The return values from this function are intended to be directly returned * from poll handler in driver. */ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { - unsigned long flags; - unsigned int ret; + struct video_device *vfd = video_devdata(file); + unsigned long req_events = poll_requested_events(wait); struct vb2_buffer *vb = NULL; + unsigned int res = 0; + unsigned long flags; + + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { + struct v4l2_fh *fh = file->private_data; + + if (v4l2_event_pending(fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->wait, wait); + } /* * Start file I/O emulator only if streaming API has not been used yet. */ if (q->num_buffers == 0 && q->fileio == NULL) { - if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ)) { - ret = __vb2_init_fileio(q, 1); - if (ret) - return POLLERR; + if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && + (req_events & (POLLIN | POLLRDNORM))) { + if (__vb2_init_fileio(q, 1)) + return res | POLLERR; } - if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) { - ret = __vb2_init_fileio(q, 0); - if (ret) - return POLLERR; + if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && + (req_events & (POLLOUT | POLLWRNORM))) { + if (__vb2_init_fileio(q, 0)) + return res | POLLERR; /* * Write to OUTPUT queue can be done immediately. */ - return POLLOUT | POLLWRNORM; + return res | POLLOUT | POLLWRNORM; } } @@ -1675,7 +1692,7 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) * There is nothing to wait for if no buffers have already been queued. */ if (list_empty(&q->queued_list)) - return POLLERR; + return res | POLLERR; poll_wait(file, &q->done_wq, wait); @@ -1690,10 +1707,11 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) if (vb && (vb->state == VB2_BUF_STATE_DONE || vb->state == VB2_BUF_STATE_ERROR)) { - return (V4L2_TYPE_IS_OUTPUT(q->type)) ? POLLOUT | POLLWRNORM : - POLLIN | POLLRDNORM; + return (V4L2_TYPE_IS_OUTPUT(q->type)) ? + res | POLLOUT | POLLWRNORM : + res | POLLIN | POLLRDNORM; } - return 0; + return res; } EXPORT_SYMBOL_GPL(vb2_poll); diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 5e8b0710105b..ff39eb2f2d7b 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -177,6 +177,7 @@ struct vivi_dev { struct v4l2_ctrl *menu; struct v4l2_ctrl *string; struct v4l2_ctrl *bitmask; + struct v4l2_ctrl *int_menu; spinlock_t slock; struct mutex mutex; @@ -503,6 +504,10 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->boolean->cur.val, dev->menu->qmenu[dev->menu->cur.val], dev->string->cur.string); + snprintf(str, sizeof(str), " integer_menu %lld, value %d ", + dev->int_menu->qmenu_int[dev->int_menu->cur.val], + dev->int_menu->cur.val); + gen_text(dev, vbuf, line++ * 16, 16, str); mutex_unlock(&dev->ctrl_handler.lock); gen_text(dev, vbuf, line++ * 16, 16, str); if (dev->button_pressed) { @@ -1039,17 +1044,10 @@ static unsigned int vivi_poll(struct file *file, struct poll_table_struct *wait) { struct vivi_dev *dev = video_drvdata(file); - struct v4l2_fh *fh = file->private_data; struct vb2_queue *q = &dev->vb_vidq; - unsigned int res; dprintk(dev, 1, "%s\n", __func__); - res = vb2_poll(q, file, wait); - if (v4l2_event_pending(fh)) - res |= POLLPRI; - else - poll_wait(file, &fh->wait, wait); - return res; + return vb2_poll(q, file, wait); } static int vivi_close(struct file *file) @@ -1165,6 +1163,22 @@ static const struct v4l2_ctrl_config vivi_ctrl_bitmask = { .step = 0, }; +static const s64 vivi_ctrl_int_menu_values[] = { + 1, 1, 2, 3, 5, 8, 13, 21, 42, +}; + +static const struct v4l2_ctrl_config vivi_ctrl_int_menu = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 7, + .name = "Integer menu", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .min = 1, + .max = 8, + .def = 4, + .menu_skip_mask = 0x02, + .qmenu_int = vivi_ctrl_int_menu_values, +}; + static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, @@ -1275,6 +1289,7 @@ static int __init vivi_create_instance(int inst) dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL); dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL); dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL); + dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL); if (hdl->error) { ret = hdl->error; goto unreg_dev; diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 4c09ab781ec3..c57310931810 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -1131,8 +1131,14 @@ static int setup_fbuffer(struct zoran_fh *fh, } -static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height, - struct v4l2_clip __user *clips, int clipcount, void __user *bitmap) +static int setup_window(struct zoran_fh *fh, + int x, + int y, + int width, + int height, + struct v4l2_clip __user *clips, + unsigned int clipcount, + void __user *bitmap) { struct zoran *zr = fh->zr; struct v4l2_clip *vcp = NULL; @@ -1155,6 +1161,14 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height return -EINVAL; } + if (clipcount > 2048) { + dprintk(1, + KERN_ERR + "%s: %s - invalid clipcount\n", + ZR_DEVNAME(zr), __func__); + return -EINVAL; + } + /* * The video front end needs 4-byte alinged line sizes, we correct that * silently here if necessary @@ -1218,7 +1232,7 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height (width * height + 7) / 8)) { return -EFAULT; } - } else if (clipcount > 0) { + } else if (clipcount) { /* write our own bitmap from the clips */ vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4)); if (vcp == NULL) { diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 9f1f27e7c86e..4511420849bc 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -269,7 +269,7 @@ out: return ret; } -static inline unsigned long calc_vm_may_flags(unsigned long prot) +static inline vm_flags_t calc_vm_may_flags(unsigned long prot) { return _calc_vm_trans(prot, PROT_READ, VM_MAYREAD) | _calc_vm_trans(prot, PROT_WRITE, VM_MAYWRITE) | diff --git a/drivers/staging/media/as102/as102_fw.c b/drivers/staging/media/as102/as102_fw.c index 1075fb1df0d9..b9670ee41b4e 100644 --- a/drivers/staging/media/as102/as102_fw.c +++ b/drivers/staging/media/as102/as102_fw.c @@ -230,11 +230,8 @@ int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) pr_info("%s: firmware: %s loaded with success\n", DRIVER_NAME, fw2); error: - /* free data buffer */ kfree(cmd_buf); - /* release firmware if needed */ - if (firmware != NULL) - release_firmware(firmware); + release_firmware(firmware); LEAVE(); return errno; diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 5f7f8cd3a661..083219da3a39 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -70,10 +70,6 @@ static ssize_t vfd_write(struct file *file, const char __user *buf, static int ir_open(void *data); static void ir_close(void *data); -/* Driver init/exit prototypes */ -static int __init imon_init(void); -static void __exit imon_exit(void); - /*** G L O B A L S ***/ #define IMON_DATA_BUF_SZ 35 diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index 74421043b954..d5bc35aea1c1 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -80,10 +80,6 @@ static ssize_t vfd_write(struct file *file, const char *buf, static int ir_open(void *data); static void ir_close(void *data); -/* Driver init/exit prototypes */ -static int __init sasem_init(void); -static void __exit sasem_exit(void); - /*** G L O B A L S ***/ #define SASEM_DATA_BUF_SZ 32 diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index d776adb2da67..104ae9c81251 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -543,11 +543,11 @@ done: return ret; } +/* called with &queue_irqlock held.. */ static struct uvc_buffer * uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) { struct uvc_buffer *nextbuf; - unsigned long flags; if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && buf->buf.length != buf->buf.bytesused) { @@ -556,14 +556,12 @@ uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) return buf; } - spin_lock_irqsave(&queue->irqlock, flags); list_del(&buf->queue); if (!list_empty(&queue->irqqueue)) nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, queue); else nextbuf = NULL; - spin_unlock_irqrestore(&queue->irqlock, flags); buf->buf.sequence = queue->sequence++; do_gettimeofday(&buf->buf.timestamp); |