diff options
Diffstat (limited to 'drivers/media/common/tuners')
-rw-r--r-- | drivers/media/common/tuners/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/common/tuners/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/common/tuners/max2165.c | 442 | ||||
-rw-r--r-- | drivers/media/common/tuners/max2165.h | 48 | ||||
-rw-r--r-- | drivers/media/common/tuners/max2165_priv.h | 60 | ||||
-rw-r--r-- | drivers/media/common/tuners/mxl5005s.c | 5 | ||||
-rw-r--r-- | drivers/media/common/tuners/mxl5005s.h | 4 | ||||
-rw-r--r-- | drivers/media/common/tuners/mxl5007t.c | 2 | ||||
-rw-r--r-- | drivers/media/common/tuners/tda18271-common.c | 16 | ||||
-rw-r--r-- | drivers/media/common/tuners/tda18271-fe.c | 114 | ||||
-rw-r--r-- | drivers/media/common/tuners/tda18271-maps.c | 1 | ||||
-rw-r--r-- | drivers/media/common/tuners/tda18271-priv.h | 47 | ||||
-rw-r--r-- | drivers/media/common/tuners/tda18271.h | 12 | ||||
-rw-r--r-- | drivers/media/common/tuners/tda8290.c | 1 | ||||
-rw-r--r-- | drivers/media/common/tuners/tda9887.c | 2 | ||||
-rw-r--r-- | drivers/media/common/tuners/xc5000.c | 97 | ||||
-rw-r--r-- | drivers/media/common/tuners/xc5000.h | 6 |
17 files changed, 792 insertions, 73 deletions
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 607d319ce8ed..409a4261e5b5 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -172,4 +172,11 @@ config MEDIA_TUNER_MC44S803 help Say Y here to support the Freescale MC44S803 based tuners +config MEDIA_TUNER_MAX2165 + tristate "Maxim MAX2165 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + A driver for the silicon tuner MAX2165 from Maxim. + endif # MEDIA_TUNER_CUSTOMISE diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 4132b2be79e5..a5438523f30d 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o +obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c new file mode 100644 index 000000000000..3d03640cf1fe --- /dev/null +++ b/drivers/media/common/tuners/max2165.c @@ -0,0 +1,442 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong <davidtlwong@gmail.com> + * + * 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 <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/videodev2.h> +#include <linux/delay.h> +#include <linux/dvb/frontend.h> +#include <linux/i2c.h> + +#include "dvb_frontend.h" + +#include "max2165.h" +#include "max2165_priv.h" +#include "tuner-i2c.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "max2165: " args); \ + } while (0) + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +static int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; + + msg.addr = priv->config->i2c_address; + + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg, 1); + + if (ret != 1) + dprintk(KERN_DEBUG "%s: error reg=0x%x, data=0x%x, ret=%i\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EIO : 0; +} + +static int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data) +{ + int ret; + u8 dev_addr = priv->config->i2c_address; + + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 }, + { .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }, + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret != 2) { + dprintk(KERN_DEBUG "%s: error reg=0x%x, ret=%i\n", + __func__, reg, ret); + return -EIO; + } + + *p_data = b1[0]; + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, b1[0]); + return 0; +} + +static int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg, + u8 mask, u8 data) +{ + int ret; + u8 v; + + data &= mask; + ret = max2165_read_reg(priv, reg, &v); + if (ret != 0) + return ret; + v &= ~mask; + v |= data; + ret = max2165_write_reg(priv, reg, v); + + return ret; +} + +static int max2165_read_rom_table(struct max2165_priv *priv) +{ + u8 dat[3]; + int i; + + for (i = 0; i < 3; i++) { + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1); + max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]); + } + + priv->tf_ntch_low_cfg = dat[0] >> 4; + priv->tf_ntch_hi_cfg = dat[0] & 0x0F; + priv->tf_balun_low_ref = dat[1] & 0x0F; + priv->tf_balun_hi_ref = dat[1] >> 4; + priv->bb_filter_7mhz_cfg = dat[2] & 0x0F; + priv->bb_filter_8mhz_cfg = dat[2] >> 4; + + dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg); + dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg); + dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref); + dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref); + dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg); + dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg); + + return 0; +} + +static int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/) +{ + u8 v; + + v = (osc / 2); + if (v == 2) + v = 0x7; + else + v -= 8; + + max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v); + + return 0; +} + +static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw) +{ + u8 val; + + if (bw == BANDWIDTH_8_MHZ) + val = priv->bb_filter_8mhz_cfg; + else + val = priv->bb_filter_7mhz_cfg; + + max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4); + + return 0; +} + +int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction) +{ + u32 remainder; + u32 q, f = 0; + int i; + + if (0 == divisor) + return -1; + + q = dividend / divisor; + remainder = dividend - q * divisor; + + for (i = 0; i < 31; i++) { + remainder <<= 1; + if (remainder >= divisor) { + f += 1; + remainder -= divisor; + } + f <<= 1; + } + + *quotient = q; + *fraction = f; + + return 0; +} + +static int max2165_set_rf(struct max2165_priv *priv, u32 freq) +{ + u8 tf; + u8 tf_ntch; + u32 t; + u32 quotient, fraction; + + /* Set PLL divider according to RF frequency */ + fixpt_div32(freq / 1000, priv->config->osc_clk * 1000, + "ient, &fraction); + + /* 20-bit fraction */ + fraction >>= 12; + + max2165_write_reg(priv, REG_NDIV_INT, quotient); + max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16); + max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8); + max2165_write_reg(priv, REG_NDIV_FRAC0, fraction); + + /* Norch Filter */ + tf_ntch = (freq < 725000000) ? + priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg; + + /* Tracking filter balun */ + t = priv->tf_balun_low_ref; + t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref) + * (freq / 1000 - 470000) / (780000 - 470000); + + tf = t; + dprintk("tf = %X\n", tf); + tf |= tf_ntch << 4; + + max2165_write_reg(priv, REG_TRACK_FILTER, tf); + + return 0; +} + +static void max2165_debug_status(struct max2165_priv *priv) +{ + u8 status, autotune; + u8 auto_vco_success, auto_vco_active; + u8 pll_locked; + u8 dc_offset_low, dc_offset_hi; + u8 signal_lv_over_threshold; + u8 vco, vco_sub_band, adc; + + max2165_read_reg(priv, REG_STATUS, &status); + max2165_read_reg(priv, REG_AUTOTUNE, &autotune); + + auto_vco_success = (status >> 6) & 0x01; + auto_vco_active = (status >> 5) & 0x01; + pll_locked = (status >> 4) & 0x01; + dc_offset_low = (status >> 3) & 0x01; + dc_offset_hi = (status >> 2) & 0x01; + signal_lv_over_threshold = status & 0x01; + + vco = autotune >> 6; + vco_sub_band = (autotune >> 3) & 0x7; + adc = autotune & 0x7; + + dprintk("auto VCO active: %d, auto VCO success: %d\n", + auto_vco_active, auto_vco_success); + dprintk("PLL locked: %d\n", pll_locked); + dprintk("DC offset low: %d, DC offset high: %d\n", + dc_offset_low, dc_offset_hi); + dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold); + dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc); +} + +static int max2165_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct max2165_priv *priv = fe->tuner_priv; + int ret; + + dprintk("%s() frequency=%d (Hz)\n", __func__, params->frequency); + if (fe->ops.info.type == FE_ATSC) { + return -EINVAL; + } else if (fe->ops.info.type == FE_OFDM) { + dprintk("%s() OFDM\n", __func__); + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + return -EINVAL; + case BANDWIDTH_7_MHZ: + case BANDWIDTH_8_MHZ: + priv->frequency = params->frequency; + priv->bandwidth = params->u.ofdm.bandwidth; + break; + default: + printk(KERN_ERR "MAX2165 bandwidth not set!\n"); + return -EINVAL; + } + } else { + printk(KERN_ERR "MAX2165 modulation type not supported!\n"); + return -EINVAL; + } + + dprintk("%s() frequency=%d\n", __func__, priv->frequency); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + max2165_set_bandwidth(priv, priv->bandwidth); + ret = max2165_set_rf(priv, priv->frequency); + mdelay(50); + max2165_debug_status(priv); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 0) + return -EREMOTEIO; + + return 0; +} + +static int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + *freq = priv->frequency; + return 0; +} + +static int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + *bw = priv->bandwidth; + return 0; +} + +static int max2165_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct max2165_priv *priv = fe->tuner_priv; + u16 lock_status = 0; + + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + max2165_debug_status(priv); + *status = lock_status; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_sleep(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return 0; +} + +static int max2165_init(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* Setup initial values */ + /* Fractional Mode on */ + max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18); + /* LNA on */ + max2165_write_reg(priv, REG_LNA, 0x01); + max2165_write_reg(priv, REG_PLL_CFG, 0x7A); + max2165_write_reg(priv, REG_TEST, 0x08); + max2165_write_reg(priv, REG_SHUTDOWN, 0x40); + max2165_write_reg(priv, REG_VCO_CTRL, 0x84); + max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3); + max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75); + max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00); + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00); + + max2165_set_osc(priv, priv->config->osc_clk); + + max2165_read_rom_table(priv); + + max2165_set_bandwidth(priv, BANDWIDTH_8_MHZ); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_release(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + kfree(priv); + fe->tuner_priv = NULL; + + return 0; +} + +static const struct dvb_tuner_ops max2165_tuner_ops = { + .info = { + .name = "Maxim MAX2165", + .frequency_min = 470000000, + .frequency_max = 780000000, + .frequency_step = 50000, + }, + + .release = max2165_release, + .init = max2165_init, + .sleep = max2165_sleep, + + .set_params = max2165_set_params, + .set_analog_params = NULL, + .get_frequency = max2165_get_frequency, + .get_bandwidth = max2165_get_bandwidth, + .get_status = max2165_get_status +}; + +struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg) +{ + struct max2165_priv *priv = NULL; + + dprintk("%s(%d-%04x)\n", __func__, + i2c ? i2c_adapter_id(i2c) : -1, + cfg ? cfg->i2c_address : -1); + + priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + priv->config = cfg; + priv->i2c = i2c; + fe->tuner_priv = priv; + + max2165_init(fe); + max2165_debug_status(priv); + + return fe; +} +EXPORT_SYMBOL(max2165_attach); + +MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); +MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/max2165.h b/drivers/media/common/tuners/max2165.h new file mode 100644 index 000000000000..c063c36a93d3 --- /dev/null +++ b/drivers/media/common/tuners/max2165.h @@ -0,0 +1,48 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong <davidtlwong@gmail.com> + * + * 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. + */ + +#ifndef __MAX2165_H__ +#define __MAX2165_H__ + +struct dvb_frontend; +struct i2c_adapter; + +struct max2165_config { + u8 i2c_address; + u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */ +}; + +#if defined(CONFIG_MEDIA_TUNER_MAX2165) || \ + (defined(CONFIG_MEDIA_TUNER_MAX2165_MODULE) && defined(MODULE)) +extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg); +#else +static inline struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/max2165_priv.h b/drivers/media/common/tuners/max2165_priv.h new file mode 100644 index 000000000000..91bbe021a08d --- /dev/null +++ b/drivers/media/common/tuners/max2165_priv.h @@ -0,0 +1,60 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong <davidtlwong@gmail.com> + * + * 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. + */ + +#ifndef __MAX2165_PRIV_H__ +#define __MAX2165_PRIV_H__ + +#define REG_NDIV_INT 0x00 +#define REG_NDIV_FRAC2 0x01 +#define REG_NDIV_FRAC1 0x02 +#define REG_NDIV_FRAC0 0x03 +#define REG_TRACK_FILTER 0x04 +#define REG_LNA 0x05 +#define REG_PLL_CFG 0x06 +#define REG_TEST 0x07 +#define REG_SHUTDOWN 0x08 +#define REG_VCO_CTRL 0x09 +#define REG_BASEBAND_CTRL 0x0A +#define REG_DC_OFFSET_CTRL 0x0B +#define REG_DC_OFFSET_DAC 0x0C +#define REG_ROM_TABLE_ADDR 0x0D + +/* Read Only Registers */ +#define REG_ROM_TABLE_DATA 0x10 +#define REG_STATUS 0x11 +#define REG_AUTOTUNE 0x12 + +struct max2165_priv { + struct max2165_config *config; + struct i2c_adapter *i2c; + + u32 frequency; + u32 bandwidth; + + u8 tf_ntch_low_cfg; + u8 tf_ntch_hi_cfg; + u8 tf_balun_low_ref; + u8 tf_balun_hi_ref; + u8 bb_filter_7mhz_cfg; + u8 bb_filter_8mhz_cfg; +}; + +#endif diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c index 0803dab58fff..605e28b73263 100644 --- a/drivers/media/common/tuners/mxl5005s.c +++ b/drivers/media/common/tuners/mxl5005s.c @@ -2789,7 +2789,10 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq) /* add for 2.6.5 Special setting for QAM */ if (state->Mod_Type == MXL_QAM) { - if (state->RF_IN < 680000000) + if (state->config->qam_gain != 0) + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, + state->config->qam_gain); + else if (state->RF_IN < 680000000) status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); else status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); diff --git a/drivers/media/common/tuners/mxl5005s.h b/drivers/media/common/tuners/mxl5005s.h index 7ac6815b30aa..fc8a1ffc53b4 100644 --- a/drivers/media/common/tuners/mxl5005s.h +++ b/drivers/media/common/tuners/mxl5005s.h @@ -108,6 +108,10 @@ struct mxl5005s_config { #define MXL_LOW_IF 1 u8 if_mode; + /* Some boards need to override the built-in logic for determining + the gain when in QAM mode (the HVR-1600 is one such case) */ + u8 qam_gain; + /* Stuff I don't know what to do with */ u8 AgcMasterByte; }; diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c index 2d02698d4f4f..7eb1bf75cd07 100644 --- a/drivers/media/common/tuners/mxl5007t.c +++ b/drivers/media/common/tuners/mxl5007t.c @@ -196,7 +196,7 @@ static void copy_reg_bits(struct reg_pair_t *reg_pair1, i = j = 0; while (reg_pair1[i].reg || reg_pair1[i].val) { - while (reg_pair2[j].reg || reg_pair2[j].reg) { + while (reg_pair2[j].reg || reg_pair2[j].val) { if (reg_pair1[i].reg != reg_pair2[j].reg) { j++; continue; diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c index 155c93eb75da..e1f678281a58 100644 --- a/drivers/media/common/tuners/tda18271-common.c +++ b/drivers/media/common/tuners/tda18271-common.c @@ -326,12 +326,24 @@ int tda18271_init_regs(struct dvb_frontend *fe) regs[R_EB22] = 0x48; regs[R_EB23] = 0xb0; - if (priv->small_i2c) { + switch (priv->small_i2c) { + case TDA18271_08_BYTE_CHUNK_INIT: + tda18271_write_regs(fe, 0x00, 0x08); + tda18271_write_regs(fe, 0x08, 0x08); + tda18271_write_regs(fe, 0x10, 0x08); + tda18271_write_regs(fe, 0x18, 0x08); + tda18271_write_regs(fe, 0x20, 0x07); + break; + case TDA18271_16_BYTE_CHUNK_INIT: tda18271_write_regs(fe, 0x00, 0x10); tda18271_write_regs(fe, 0x10, 0x10); tda18271_write_regs(fe, 0x20, 0x07); - } else + break; + case TDA18271_39_BYTE_CHUNK_INIT: + default: tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS); + break; + } /* setup agc1 gain */ regs[R_EB17] = 0x00; diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 3a50ce96fcb9..b2e15456d5f3 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -256,8 +256,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, struct tda18271_priv *priv = fe->tuner_priv; struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; - int tm_current, rfcal_comp, approx, i, ret; - u8 dc_over_dt, rf_tab; + int i, ret; + u8 tm_current, dc_over_dt, rf_tab; + s32 rfcal_comp, approx; /* power up */ ret = tda18271_set_standby_mode(fe, 0, 0, 0); @@ -277,11 +278,11 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, return i; if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { - approx = map[i].rf_a1 * - (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab; + approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) + + map[i].rf_b1 + rf_tab; } else { - approx = map[i].rf_a2 * - (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab; + approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) + + map[i].rf_b2 + rf_tab; } if (approx < 0) @@ -292,9 +293,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); /* calculate temperature compensation */ - rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal) / 1000; + rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000; - regs[R_EB14] = approx + rfcal_comp; + regs[R_EB14] = (unsigned char)(approx + rfcal_comp); ret = tda18271_write_regs(fe, R_EB14, 1); fail: return ret; @@ -572,6 +573,7 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; int bcal, rf, i; + s32 divisor, dividend; #define RF1 0 #define RF2 1 #define RF3 2 @@ -610,20 +612,22 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) switch (rf) { case RF1: map[i].rf_a1 = 0; - map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1]; + map[i].rf_b1 = (s32)(prog_cal[RF1] - prog_tab[RF1]); map[i].rf1 = rf_freq[RF1] / 1000; break; case RF2: - map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] - - prog_cal[RF1] + prog_tab[RF1]) / - (s32)((rf_freq[RF2] - rf_freq[RF1]) / 1000); + dividend = (s32)(prog_cal[RF2] - prog_tab[RF2]) - + (s32)(prog_cal[RF1] + prog_tab[RF1]); + divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000; + map[i].rf_a1 = (dividend / divisor); map[i].rf2 = rf_freq[RF2] / 1000; break; case RF3: - map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] - - prog_cal[RF2] + prog_tab[RF2]) / - (s32)((rf_freq[RF3] - rf_freq[RF2]) / 1000); - map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2]; + dividend = (s32)(prog_cal[RF3] - prog_tab[RF3]) - + (s32)(prog_cal[RF2] + prog_tab[RF2]); + divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000; + map[i].rf_a2 = (dividend / divisor); + map[i].rf_b2 = (s32)(prog_cal[RF2] - prog_tab[RF2]); map[i].rf3 = rf_freq[RF3] / 1000; break; default: @@ -1181,6 +1185,48 @@ static int tda18271_get_id(struct dvb_frontend *fe) return ret; } +static int tda18271_setup_configuration(struct dvb_frontend *fe, + struct tda18271_config *cfg) +{ + struct tda18271_priv *priv = fe->tuner_priv; + + priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; + priv->role = (cfg) ? cfg->role : TDA18271_MASTER; + priv->config = (cfg) ? cfg->config : 0; + priv->small_i2c = (cfg) ? + cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT; + priv->output_opt = (cfg) ? + cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; + + return 0; +} + +static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg) +{ + /* tda18271_cal_on_startup == -1 when cal module option is unset */ + return ((tda18271_cal_on_startup == -1) ? + /* honor configuration setting */ + ((cfg) && (cfg->rf_cal_on_startup)) : + /* module option overrides configuration setting */ + (tda18271_cal_on_startup)) ? 1 : 0; +} + +static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; + + tda18271_setup_configuration(fe, cfg); + + if (tda18271_need_cal_on_startup(cfg)) + tda18271_init(fe); + + /* override default std map with values in config struct */ + if ((cfg) && (cfg->std_map)) + tda18271_update_std_map(fe, cfg->std_map); + + return 0; +} + static struct dvb_tuner_ops tda18271_tuner_ops = { .info = { .name = "NXP TDA18271HD", @@ -1193,6 +1239,7 @@ static struct dvb_tuner_ops tda18271_tuner_ops = { .set_params = tda18271_set_params, .set_analog_params = tda18271_set_analog_params, .release = tda18271_release, + .set_config = tda18271_set_config, .get_frequency = tda18271_get_frequency, .get_bandwidth = tda18271_get_bandwidth, }; @@ -1213,33 +1260,14 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, case 0: goto fail; case 1: - { /* new tuner instance */ - int rf_cal_on_startup; - - priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; - priv->role = (cfg) ? cfg->role : TDA18271_MASTER; - priv->config = (cfg) ? cfg->config : 0; - priv->small_i2c = (cfg) ? cfg->small_i2c : 0; - priv->output_opt = (cfg) ? - cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; - - /* tda18271_cal_on_startup == -1 when cal - * module option is unset */ - if (tda18271_cal_on_startup == -1) { - /* honor attach-time configuration */ - rf_cal_on_startup = - ((cfg) && (cfg->rf_cal_on_startup)) ? 1 : 0; - } else { - /* module option overrides attach configuration */ - rf_cal_on_startup = tda18271_cal_on_startup; - } + fe->tuner_priv = priv; + + tda18271_setup_configuration(fe, cfg); priv->cal_initialized = false; mutex_init(&priv->lock); - fe->tuner_priv = priv; - if (tda_fail(tda18271_get_id(fe))) goto fail; @@ -1249,12 +1277,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, mutex_lock(&priv->lock); tda18271_init_regs(fe); - if ((rf_cal_on_startup) && (priv->id == TDA18271HDC2)) + if ((tda18271_need_cal_on_startup(cfg)) && + (priv->id == TDA18271HDC2)) tda18271c2_rf_cal_init(fe); mutex_unlock(&priv->lock); break; - } default: /* existing tuner instance */ fe->tuner_priv = priv; @@ -1271,7 +1299,11 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->small_i2c = cfg->small_i2c; if (cfg->output_opt) priv->output_opt = cfg->output_opt; + if (cfg->std_map) + tda18271_update_std_map(fe, cfg->std_map); } + if (tda18271_need_cal_on_startup(cfg)) + tda18271_init(fe); break; } @@ -1298,7 +1330,7 @@ EXPORT_SYMBOL_GPL(tda18271_attach); MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.3"); +MODULE_VERSION("0.4"); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c index e21fdeff3ddf..e7f84c705da8 100644 --- a/drivers/media/common/tuners/tda18271-maps.c +++ b/drivers/media/common/tuners/tda18271-maps.c @@ -978,6 +978,7 @@ static struct tda18271_cid_target_map tda18271_cid_target[] = { int tda18271_lookup_cid_target(struct dvb_frontend *fe, u32 *freq, u8 *cid_target, u16 *count_limit) { + struct tda18271_priv *priv = fe->tuner_priv; int i = 0; while ((tda18271_cid_target[i].rfmax * 1000) < *freq) { diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h index 2bee229acd91..9589ab0576d2 100644 --- a/drivers/media/common/tuners/tda18271-priv.h +++ b/drivers/media/common/tuners/tda18271-priv.h @@ -80,10 +80,10 @@ struct tda18271_rf_tracking_filter_cal { u32 rf1; u32 rf2; u32 rf3; - int rf_a1; - int rf_b1; - int rf_a2; - int rf_b2; + s32 rf_a1; + s32 rf_b1; + s32 rf_a2; + s32 rf_b2; }; enum tda18271_pll { @@ -109,11 +109,12 @@ struct tda18271_priv { enum tda18271_i2c_gate gate; enum tda18271_ver id; enum tda18271_output_options output_opt; + enum tda18271_small_i2c small_i2c; unsigned int config; /* interface to saa713x / tda829x */ - unsigned int tm_rfcal; unsigned int cal_initialized:1; - unsigned int small_i2c:1; + + u8 tm_rfcal; struct tda18271_map_layout *maps; struct tda18271_std_map std; @@ -135,27 +136,37 @@ extern int tda18271_debug; #define DBG_ADV 8 #define DBG_CAL 16 -#define tda_printk(kern, fmt, arg...) \ - printk(kern "%s: " fmt, __func__, ##arg) - -#define tda_dprintk(lvl, fmt, arg...) do {\ +#define tda_printk(st, kern, fmt, arg...) do {\ + if (st) { \ + struct tda18271_priv *state = st; \ + printk(kern "%s: [%d-%04x|%s] " fmt, __func__, \ + i2c_adapter_id(state->i2c_props.adap), \ + state->i2c_props.addr, \ + (state->role == TDA18271_MASTER) \ + ? "M" : "S", ##arg); \ + } else \ + printk(kern "%s: " fmt, __func__, ##arg); \ +} while (0) + +#define tda_dprintk(st, lvl, fmt, arg...) do {\ if (tda18271_debug & lvl) \ - tda_printk(KERN_DEBUG, fmt, ##arg); } while (0) + tda_printk(st, KERN_DEBUG, fmt, ##arg); } while (0) #define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg) -#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg) -#define tda_err(fmt, arg...) tda_printk(KERN_ERR, fmt, ##arg) -#define tda_dbg(fmt, arg...) tda_dprintk(DBG_INFO, fmt, ##arg) -#define tda_map(fmt, arg...) tda_dprintk(DBG_MAP, fmt, ##arg) -#define tda_reg(fmt, arg...) tda_dprintk(DBG_REG, fmt, ##arg) -#define tda_cal(fmt, arg...) tda_dprintk(DBG_CAL, fmt, ##arg) +#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg) +#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg) +#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg) +#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg) +#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg) +#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg) #define tda_fail(ret) \ ({ \ int __ret; \ __ret = (ret < 0); \ if (__ret) \ - tda_printk(KERN_ERR, "error %d on line %d\n", ret, __LINE__);\ + tda_printk(priv, KERN_ERR, \ + "error %d on line %d\n", ret, __LINE__); \ __ret; \ }) diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h index 323f2912128d..d7fcc36dc6e6 100644 --- a/drivers/media/common/tuners/tda18271.h +++ b/drivers/media/common/tuners/tda18271.h @@ -78,6 +78,12 @@ enum tda18271_output_options { TDA18271_OUTPUT_XT_OFF = 2, }; +enum tda18271_small_i2c { + TDA18271_39_BYTE_CHUNK_INIT = 0, + TDA18271_16_BYTE_CHUNK_INIT = 1, + TDA18271_08_BYTE_CHUNK_INIT = 2, +}; + struct tda18271_config { /* override default if freq / std settings (optional) */ struct tda18271_std_map *std_map; @@ -91,12 +97,12 @@ struct tda18271_config { /* output options that can be disabled */ enum tda18271_output_options output_opt; + /* some i2c providers cant write all 39 registers at once */ + enum tda18271_small_i2c small_i2c; + /* force rf tracking filter calibration on startup */ unsigned int rf_cal_on_startup:1; - /* some i2c providers cant write all 39 registers at once */ - unsigned int small_i2c:1; - /* interface to saa713x / tda829x */ unsigned int config; }; diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index 064d14c8d7b2..c190b0dedee4 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -33,6 +33,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); static int deemphasis_50; +module_param(deemphasis_50, int, 0644); MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis"); /* ---------------------------------------------------------------------- */ diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c index 544cdbe88a6c..a71c100c95df 100644 --- a/drivers/media/common/tuners/tda9887.c +++ b/drivers/media/common/tuners/tda9887.c @@ -463,7 +463,7 @@ static int tda9887_set_insmod(struct dvb_frontend *fe) buf[1] &= ~cQSS; } - if (adjust >= 0x00 && adjust < 0x20) { + if (adjust < 0x20) { buf[2] &= ~cTopMask; buf[2] |= adjust; } diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index f4ffcdc9b848..432003dded7c 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -61,6 +61,7 @@ struct xc5000_priv { u32 bandwidth; u8 video_standard; u8 rf_mode; + u8 radio_input; }; /* Misc Defines */ @@ -632,8 +633,12 @@ static int xc5000_set_params(struct dvb_frontend *fe, struct xc5000_priv *priv = fe->tuner_priv; int ret; - if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) - xc_load_fw_and_init_tuner(fe); + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { + dprintk(1, "Unable to load firmware and init tuner\n"); + return -EINVAL; + } + } dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency); @@ -739,15 +744,12 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) return ret; } -static int xc5000_set_analog_params(struct dvb_frontend *fe, +static int xc5000_set_tv_freq(struct dvb_frontend *fe, struct analog_parameters *params) { struct xc5000_priv *priv = fe->tuner_priv; int ret; - if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) - xc_load_fw_and_init_tuner(fe); - dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", __func__, params->frequency); @@ -827,6 +829,86 @@ tune_channel: return 0; } +static int xc5000_set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + u8 radio_input; + + dprintk(1, "%s() frequency=%d (in units of khz)\n", + __func__, params->frequency); + + if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) { + dprintk(1, "%s() radio input not configured\n", __func__); + return -EINVAL; + } + + if (priv->radio_input == XC5000_RADIO_FM1) + radio_input = FM_Radio_INPUT1; + else if (priv->radio_input == XC5000_RADIO_FM2) + radio_input = FM_Radio_INPUT2; + else { + dprintk(1, "%s() unknown radio input %d\n", __func__, + priv->radio_input); + return -EINVAL; + } + + priv->freq_hz = params->frequency * 125 / 2; + + priv->rf_mode = XC_RF_MODE_AIR; + + ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode, + XC5000_Standard[radio_input].AudioMode); + + if (ret != XC_RESULT_SUCCESS) { + printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); + return -EREMOTEIO; + } + + ret = xc_SetSignalSource(priv, priv->rf_mode); + if (ret != XC_RESULT_SUCCESS) { + printk(KERN_ERR + "xc5000: xc_SetSignalSource(%d) failed\n", + priv->rf_mode); + return -EREMOTEIO; + } + + xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG); + + return 0; +} + +static int xc5000_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { + dprintk(1, "Unable to load firmware and init tuner\n"); + return -EINVAL; + } + } + + switch (params->mode) { + case V4L2_TUNER_RADIO: + ret = xc5000_set_radio_freq(fe, params); + break; + case V4L2_TUNER_ANALOG_TV: + case V4L2_TUNER_DIGITAL_TV: + ret = xc5000_set_tv_freq(fe, params); + break; + } + + return ret; +} + + static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc5000_priv *priv = fe->tuner_priv; @@ -1000,6 +1082,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, priv->if_khz = cfg->if_khz; } + if (priv->radio_input == 0) + priv->radio_input = cfg->radio_input; + /* Check if firmware has been loaded. It is possible that another instance of the driver has loaded the firmware. */ diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index f4c146698a00..e6d7236c9ea1 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -30,11 +30,17 @@ struct i2c_adapter; struct xc5000_config { u8 i2c_address; u32 if_khz; + u8 radio_input; }; /* xc5000 callback command */ #define XC5000_TUNER_RESET 0 +/* Possible Radio inputs */ +#define XC5000_RADIO_NOT_CONFIGURED 0 +#define XC5000_RADIO_FM1 1 +#define XC5000_RADIO_FM2 2 + /* For each bridge framework, when it attaches either analog or digital, * it has to store a reference back to its _core equivalent structure, * so that it can service the hardware by steering gpio's etc. |