From a80abc58feda48f868d748bde8c88592c2892b1d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 5 Apr 2013 14:35:18 -0300 Subject: [media] r820t: Add a tuner driver for Rafael Micro R820T silicon tuner This driver was written from scratch, based on an existing driver that it is part of rtl-sdr git tree, released under GPLv2: https://groups.google.com/forum/#!topic/ultra-cheap-sdr/Y3rBEOFtHug https://github.com/n1gp/gr-baz http://cgit.osmocom.org/rtl-sdr/plain/src/tuner_r820t.c (there are also other variants of it out there) >From what I understood from the threads, the original driver was converted to userspace from a Realtek tree. I couldn't find the original tree. However, the original driver look awkward on my eyes. So, I decided to write a new version from it from the scratch, while trying to reproduce everything found there. TODO: - After locking, the original driver seems to have some routines to improve reception. This was not implemented here yet. - RF Gain set/get is not implemented. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/Kconfig | 7 + drivers/media/tuners/Makefile | 1 + drivers/media/tuners/r820t.c | 1486 +++++++++++++++++++++++++++++++++++++++++ drivers/media/tuners/r820t.h | 55 ++ 4 files changed, 1549 insertions(+) create mode 100644 drivers/media/tuners/r820t.c create mode 100644 drivers/media/tuners/r820t.h diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index ffabd66dd14d..f6768cad001a 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -248,4 +248,11 @@ config MEDIA_TUNER_IT913X default m if !MEDIA_SUBDRV_AUTOSELECT help ITE Tech IT913x silicon tuner driver. + +config MEDIA_TUNER_R820T + tristate "Rafael Micro R820T silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Rafael Micro R820T silicon tuner driver. endmenu diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index 2ebe4b725b51..308f108eadba 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o obj-$(CONFIG_MEDIA_TUNER_IT913X) += tuner_it913x.o +obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c new file mode 100644 index 000000000000..7e02920f385a --- /dev/null +++ b/drivers/media/tuners/r820t.c @@ -0,0 +1,1486 @@ +/* + * Rafael Micro R820T driver + * + * Copyright (C) 2013 Mauro Carvalho Chehab + * + * This driver was written from scratch, based on an existing driver + * that it is part of rtl-sdr git tree, released under GPLv2: + * https://groups.google.com/forum/#!topic/ultra-cheap-sdr/Y3rBEOFtHug + * https://github.com/n1gp/gr-baz + * + * From what I understood from the threads, the original driver was converted + * to userspace from a Realtek tree. I couldn't find the original tree. + * However, the original driver look awkward on my eyes. So, I decided to + * write a new version from it from the scratch, while trying to reproduce + * everything found there. + * + * TODO: + * After locking, the original driver seems to have some routines to + * improve reception. This was not implemented here yet. + * + * RF Gain set/get is not implemented. + * + * 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. + * + */ + +#include +#include +#include +#include "tuner-i2c.h" +#include +#include "r820t.h" + +/* + * FIXME: I think that there are only 32 registers, but better safe than + * sorry. After finishing the driver, we may review it. + */ +#define REG_SHADOW_START 5 +#define NUM_REGS 27 + +#define VER_NUM 49 + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +/* + * enums and structures + */ + +enum xtal_cap_value { + XTAL_LOW_CAP_30P = 0, + XTAL_LOW_CAP_20P, + XTAL_LOW_CAP_10P, + XTAL_LOW_CAP_0P, + XTAL_HIGH_CAP_0P +}; + +struct r820t_priv { + struct list_head hybrid_tuner_instance_list; + const struct r820t_config *cfg; + struct tuner_i2c_props i2c_props; + struct mutex lock; + + u8 regs[NUM_REGS]; + u8 buf[NUM_REGS + 1]; + enum xtal_cap_value xtal_cap_sel; + u16 pll; /* kHz */ + u32 int_freq; + u8 fil_cal_code; + bool imr_done; + + /* Store current mode */ + u32 delsys; + enum v4l2_tuner_type type; + v4l2_std_id std; + u32 bw; /* in MHz */ + + bool has_lock; +}; + +struct r820t_freq_range { + u32 freq; + u8 open_d; + u8 rf_mux_ploy; + u8 tf_c; + u8 xtal_cap20p; + u8 xtal_cap10p; + u8 xtal_cap0p; + u8 imr_mem; /* Not used, currently */ +}; + +#define VCO_POWER_REF 0x02 + +/* + * Static constants + */ + +static LIST_HEAD(hybrid_tuner_instance_list); +static DEFINE_MUTEX(r820t_list_mutex); + +/* Those initial values start from REG_SHADOW_START */ +static const u8 r820t_init_array[NUM_REGS] = { + 0x83, 0x32, 0x75, /* 05 to 07 */ + 0xc0, 0x40, 0xd6, 0x6c, /* 08 to 0b */ + 0xf5, 0x63, 0x75, 0x68, /* 0c to 0f */ + 0x6c, 0x83, 0x80, 0x00, /* 10 to 13 */ + 0x0f, 0x00, 0xc0, 0x30, /* 14 to 17 */ + 0x48, 0xcc, 0x60, 0x00, /* 18 to 1b */ + 0x54, 0xae, 0x4a, 0xc0 /* 1c to 1f */ +}; + +/* Tuner frequency ranges */ +static const struct r820t_freq_range freq_ranges[] = { + { + .freq = 0, + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0xdf, /* R27[7:0] band2,band0 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 50, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0xbe, /* R27[7:0] band4,band1 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 55, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x8b, /* R27[7:0] band7,band4 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 60, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x7b, /* R27[7:0] band8,band4 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 65, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x69, /* R27[7:0] band9,band6 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 70, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x58, /* R27[7:0] band10,band7 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 75, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x44, /* R27[7:0] band11,band11 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 80, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x44, /* R27[7:0] band11,band11 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 90, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x34, /* R27[7:0] band12,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 100, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x34, /* R27[7:0] band12,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 110, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x24, /* R27[7:0] band13,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 120, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x24, /* R27[7:0] band13,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 140, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x14, /* R27[7:0] band14,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 180, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x13, /* R27[7:0] band14,band12 */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 220, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x13, /* R27[7:0] band14,band12 */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 250, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x11, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 280, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 310, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 450, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 3, + }, { + .freq = 588, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 3, + }, { + .freq = 650, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 4, + } +}; + +static int r820t_xtal_capacitor[][2] = { + { 0x0b, XTAL_LOW_CAP_30P }, + { 0x02, XTAL_LOW_CAP_20P }, + { 0x01, XTAL_LOW_CAP_10P }, + { 0x00, XTAL_LOW_CAP_0P }, + { 0x10, XTAL_HIGH_CAP_0P }, +}; + +/* + * I2C read/write code and shadow registers logic + */ +static void shadow_store(struct r820t_priv *priv, u8 reg, const u8 *val, + int len) +{ + int r = reg - REG_SHADOW_START; + + if (r < 0) { + len += r; + r = 0; + } + if (len <= 0) + return; + if (len > NUM_REGS) + len = NUM_REGS; + + tuner_dbg("%s: prev reg=%02x len=%d: %*ph\n", + __func__, r + REG_SHADOW_START, len, len, val); + + memcpy(&priv->regs[r], val, len); +} + +static int r820t_write(struct r820t_priv *priv, u8 reg, const u8 *val, + int len) +{ + int rc, size, pos = 0; + + /* Store the shadow registers */ + shadow_store(priv, reg, val, len); + + do { + if (len > priv->cfg->max_i2c_msg_len - 1) + size = priv->cfg->max_i2c_msg_len - 1; + else + size = len; + + /* Fill I2C buffer */ + priv->buf[0] = reg; + memcpy(&priv->buf[1], &val[pos], size); + + rc = tuner_i2c_xfer_send(&priv->i2c_props, priv->buf, size + 1); + if (rc != size + 1) { + tuner_info("%s: i2c wr failed=%d reg=%02x len=%d: %*ph\n", + __func__, rc, reg, size, size, &priv->buf[1]); + if (rc < 0) + return rc; + return -EREMOTEIO; + } + tuner_dbg("%s: i2c wr reg=%02x len=%d: %*ph\n", + __func__, reg, size, size, &priv->buf[1]); + + reg += size; + len -= size; + pos += size; + } while (len > 0); + + return 0; +} + +static int r820t_write_reg(struct r820t_priv *priv, u8 reg, u8 val) +{ + return r820t_write(priv, reg, &val, 1); +} + +static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, + u8 bit_mask) +{ + int r = reg - REG_SHADOW_START; + + if (r >= 0 && r < NUM_REGS) + val = (priv->regs[r] & ~bit_mask) | (val & bit_mask); + else + return -EINVAL; + + return r820t_write(priv, reg, &val, 1); +} + +static int r820_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) +{ + int rc; + u8 *p = &priv->buf[1]; + + priv->buf[0] = reg; + + rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, priv->buf, 1, p, len); + if (rc != len) { + tuner_info("%s: i2c rd failed=%d reg=%02x len=%d: %*ph\n", + __func__, rc, reg, len, len, p); + if (rc < 0) + return rc; + return -EREMOTEIO; + } + tuner_dbg("%s: i2c rd reg=%02x len=%d: %*ph\n", + __func__, reg, len, len, p); + + /* Copy data to the output buffer */ + memcpy(val, p, len); + + return 0; +} + +/* + * r820t tuning logic + */ + +static int r820t_set_mux(struct r820t_priv *priv, u32 freq) +{ + const struct r820t_freq_range *range; + int i, rc; + u8 val; + + /* Get the proper frequency range */ + freq = freq / 1000000; + for (i = 0; i < ARRAY_SIZE(freq_ranges) - 1; i++) { + if (freq < freq_ranges[i + 1].freq) + break; + } + range = &freq_ranges[i]; + + tuner_dbg("set r820t range#%d for frequency %d MHz\n", i, freq); + + /* Open Drain */ + rc = r820t_write_reg_mask(priv, 0x17, range->open_d, 0x08); + if (rc < 0) + return rc; + + /* RF_MUX,Polymux */ + rc = r820t_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3); + if (rc < 0) + return rc; + + /* TF BAND */ + rc = r820t_write_reg(priv, 0x1b, range->tf_c); + if (rc < 0) + return rc; + + /* XTAL CAP & Drive */ + switch (priv->xtal_cap_sel) { + case XTAL_LOW_CAP_30P: + case XTAL_LOW_CAP_20P: + val = range->xtal_cap20p | 0x08; + break; + case XTAL_LOW_CAP_10P: + val = range->xtal_cap10p | 0x08; + break; + case XTAL_HIGH_CAP_0P: + val = range->xtal_cap0p | 0x00; + break; + default: + case XTAL_LOW_CAP_0P: + val = range->xtal_cap0p | 0x08; + break; + } + rc = r820t_write_reg_mask(priv, 0x10, val, 0x0b); + if (rc < 0) + return rc; + + /* + * FIXME: the original driver has a logic there with preserves + * gain/phase from registers 8 and 9 reading the data from the + * registers before writing, if "IMF done". That code was sort of + * commented there, as the flag is always false. + */ + rc = r820t_write_reg_mask(priv, 0x08, 0, 0x3f); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x09, 0, 0x3f); + + return rc; +} + +static int r820t_set_pll(struct r820t_priv *priv, u32 freq) +{ + u64 tmp64, vco_freq; + int rc, i; + u32 vco_fra; /* VCO contribution by SDM (kHz) */ + u32 vco_min = 1770000; + u32 vco_max = vco_min * 2; + u32 pll_ref; + u16 n_sdm = 2; + u16 sdm = 0; + u8 mix_div = 2; + u8 div_buf = 0; + u8 div_num = 0; + u8 ni, si, nint, vco_fine_tune, val; + u8 data[5]; + + freq = freq / 1000; /* Frequency in kHz */ + + pll_ref = priv->cfg->xtal / 1000; + + tuner_dbg("set r820t pll for frequency %d kHz = %d\n", freq, pll_ref); + + /* FIXME: this seems to be a hack - probably it can be removed */ + rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x00); + if (rc < 0) + return rc; + + /* set pll autotune = 128kHz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x0c); + if (rc < 0) + return rc; + + /* set VCO current = 100 */ + rc = r820t_write_reg_mask(priv, 0x12, 0x80, 0xe0); + if (rc < 0) + return rc; + + /* Calculate divider */ + while (mix_div <= 64) { + if (((freq * mix_div) >= vco_min) && + ((freq * mix_div) < vco_max)) { + div_buf = mix_div; + while (div_buf > 2) { + div_buf = div_buf >> 1; + div_num++; + } + break; + } + mix_div = mix_div << 1; + } + + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + vco_fine_tune = (data[4] & 0x30) >> 4; + + if (vco_fine_tune > VCO_POWER_REF) + div_num = div_num - 1; + else if (vco_fine_tune < VCO_POWER_REF) + div_num = div_num + 1; + + rc = r820t_write_reg_mask(priv, 0x10, div_num << 5, 0xe0); + if (rc < 0) + return rc; + + vco_freq = (u64)(freq * (u64)mix_div); + + tmp64 = vco_freq; + do_div(tmp64, 2 * pll_ref); + nint = (u8)tmp64; + + tmp64 = vco_freq - ((u64)2) * pll_ref * nint; + do_div(tmp64, 1000); + vco_fra = (u16)(tmp64); + + pll_ref /= 1000; + + /* boundary spur prevention */ + if (vco_fra < pll_ref / 64) { + vco_fra = 0; + } else if (vco_fra > pll_ref * 127 / 64) { + vco_fra = 0; + nint++; + } else if ((vco_fra > pll_ref * 127 / 128) && (vco_fra < pll_ref)) { + vco_fra = pll_ref * 127 / 128; + } else if ((vco_fra > pll_ref) && (vco_fra < pll_ref * 129 / 128)) { + vco_fra = pll_ref * 129 / 128; + } + + if (nint > 63) { + tuner_info("No valid PLL values for %u kHz!\n", freq); + return -EINVAL; + } + + ni = (nint - 13) / 4; + si = nint - 4 * ni - 13; + + rc = r820t_write_reg(priv, 0x14, ni + (si << 6)); + if (rc < 0) + return rc; + + /* pw_sdm */ + if (!vco_fra) + val = 0x08; + else + val = 0x00; + + rc = r820t_write_reg_mask(priv, 0x12, val, 0x08); + if (rc < 0) + return rc; + + /* sdm calculator */ + while (vco_fra > 1) { + if (vco_fra > (2 * pll_ref / n_sdm)) { + sdm = sdm + 32768 / (n_sdm / 2); + vco_fra = vco_fra - 2 * pll_ref / n_sdm; + if (n_sdm >= 0x8000) + break; + } + n_sdm = n_sdm << 1; + } + + rc = r820t_write_reg_mask(priv, 0x16, sdm >> 8, 0x08); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x15, sdm & 0xff, 0x08); + if (rc < 0) + return rc; + + for (i = 0; i < 2; i++) { + /* + * FIXME: Rafael chips R620D, R828D and R828 seems to + * need 20 ms for analog TV + */ + msleep(10); + + /* Check if PLL has locked */ + rc = r820_read(priv, 0x00, data, 3); + if (rc < 0) + return rc; + if (data[2] & 0x40) + break; + + if (!i) { + /* Didn't lock. Increase VCO current */ + rc = r820t_write_reg_mask(priv, 0x12, 0x60, 0xe0); + if (rc < 0) + return rc; + } + } + + if (!(data[2] & 0x40)) { + priv->has_lock = false; + return 0; + } + + priv->has_lock = true; + tuner_dbg("tuner has lock at frequency %d kHz\n", freq); + + /* set pll autotune = 8kHz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x08, 0x08); + + return rc; +} + +static int r820t_sysfreq_sel(struct r820t_priv *priv, u32 freq, + enum v4l2_tuner_type type, + v4l2_std_id std, + u32 delsys) +{ + int rc; + u8 mixer_top, lna_top, cp_cur, div_buf_cur, lna_vth_l, mixer_vth_l; + u8 air_cable1_in, cable2_in, pre_dect, lna_discharge, filter_cur; + + tuner_dbg("adjusting tuner parameters for the standard\n"); + + switch (delsys) { + case SYS_DVBT: + if ((freq == 506000000) || (freq == 666000000) || + (freq == 818000000)) { + mixer_top = 0x14; /* mixer top:14 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + cp_cur = 0x28; /* 101, 0.2 */ + div_buf_cur = 0x20; /* 10, 200u */ + } else { + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + } + lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + filter_cur = 0x40; /* 10, low */ + break; + case SYS_DVBT2: + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + filter_cur = 0x40; /* 10, low */ + break; + case SYS_ISDBT: + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + lna_vth_l = 0x75; /* lna vth 1.04 , vtl 0.84 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + filter_cur = 0x40; /* 10, low */ + break; + default: /* DVB-T 8M */ + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + filter_cur = 0x40; /* 10, low */ + break; + } + + rc = r820t_write_reg_mask(priv, 0x1d, lna_top, 0xc7); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0xf8); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0d, lna_vth_l); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0e, mixer_vth_l); + if (rc < 0) + return rc; + + /* Air-IN only for Astrometa */ + rc = r820t_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x06, cable2_in, 0x08); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x11, cp_cur, 0x38); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x17, div_buf_cur, 0x30); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x0a, filter_cur, 0x60); + if (rc < 0) + return rc; + /* + * Original driver initializes regs 0x05 and 0x06 with the + * same value again on this point. Probably, it is just an + * error there + */ + + /* + * Set LNA + */ + + tuner_dbg("adjusting LNA parameters\n"); + if (type != V4L2_TUNER_ANALOG_TV) { + /* LNA TOP: lowest */ + rc = r820t_write_reg_mask(priv, 0x1d, 0, 0x38); + if (rc < 0) + return rc; + + /* 0: normal mode */ + rc = r820t_write_reg_mask(priv, 0x1c, 0, 0x04); + if (rc < 0) + return rc; + + /* 0: PRE_DECT off */ + rc = r820t_write_reg_mask(priv, 0x06, 0, 0x40); + if (rc < 0) + return rc; + + /* agc clk 250hz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x30, 0x30); + if (rc < 0) + return rc; + + msleep(250); + + /* write LNA TOP = 3 */ + rc = r820t_write_reg_mask(priv, 0x1d, 0x18, 0x38); + if (rc < 0) + return rc; + + /* + * write discharge mode + * FIXME: IMHO, the mask here is wrong, but it matches + * what's there at the original driver + */ + rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0x04); + if (rc < 0) + return rc; + + /* LNA discharge current */ + rc = r820t_write_reg_mask(priv, 0x1e, lna_discharge, 0x1f); + if (rc < 0) + return rc; + + /* agc clk 60hz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x20, 0x30); + if (rc < 0) + return rc; + } else { + /* PRE_DECT off */ + rc = r820t_write_reg_mask(priv, 0x06, 0, 0x40); + if (rc < 0) + return rc; + + /* write LNA TOP */ + rc = r820t_write_reg_mask(priv, 0x1d, lna_top, 0x38); + if (rc < 0) + return rc; + + /* + * write discharge mode + * FIXME: IMHO, the mask here is wrong, but it matches + * what's there at the original driver + */ + rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0x04); + if (rc < 0) + return rc; + + /* LNA discharge current */ + rc = r820t_write_reg_mask(priv, 0x1e, lna_discharge, 0x1f); + if (rc < 0) + return rc; + + /* agc clk 1Khz, external det1 cap 1u */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x30); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x04); + if (rc < 0) + return rc; + } + return 0; +} + +static int r820t_set_tv_standard(struct r820t_priv *priv, + unsigned bw, + enum v4l2_tuner_type type, + v4l2_std_id std, u32 delsys) + +{ + int rc, i; + u32 if_khz, filt_cal_lo; + u8 data[5], val; + u8 filt_gain, img_r, filt_q, hp_cor, ext_enable, loop_through; + u8 lt_att, flt_ext_widest, polyfil_cur; + bool need_calibration; + + tuner_dbg("selecting the delivery system\n"); + + if (delsys == SYS_ISDBT) { + if_khz = 4063; + filt_cal_lo = 59000; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x6a; /* 1.7m disable, +2cap, 1.25mhz */ + ext_enable = 0x40; /* r30[6], ext enable; r30[5]:0 ext at lna max */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } else { + if (bw <= 6) { + if_khz = 3570; + filt_cal_lo = 56000; /* 52000->56000 */ + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x6b; /* 1.7m disable, +2cap, 1.0mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } else if (bw == 7) { + if_khz = 4070; + filt_cal_lo = 60000; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x2b; /* 1.7m disable, +1cap, 1.0mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ +#if 0 /* 7 MHz type 2 - nor sure why/where this is used - Perhaps Australia? */ + if_khz = 4570; + filt_cal_lo = 63000; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x2a; /* 1.7m disable, +1cap, 1.25mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ +#endif + } else { + if_khz = 4570; + filt_cal_lo = 68500; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x0b; /* 1.7m disable, +0cap, 1.0mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } + } + + /* Initialize the shadow registers */ + memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); + + /* Init Flag & Xtal_check Result */ + if (priv->imr_done) + val = 1 | priv->xtal_cap_sel << 1; + else + val = 0; + rc = r820t_write_reg_mask(priv, 0x0c, val, 0x0f); + if (rc < 0) + return rc; + + /* version */ + rc = r820t_write_reg_mask(priv, 0x13, VER_NUM, 0x3f); + if (rc < 0) + return rc; + + /* for LT Gain test */ + if (type != V4L2_TUNER_ANALOG_TV) { + rc = r820t_write_reg_mask(priv, 0x1d, 0x00, 0x38); + if (rc < 0) + return rc; + msleep(1); + } + priv->int_freq = if_khz; + + /* Check if standard changed. If so, filter calibration is needed */ + if (type != priv->type) + need_calibration = true; + else if ((type == V4L2_TUNER_ANALOG_TV) && (std != priv->std)) + need_calibration = true; + else if ((type == V4L2_TUNER_DIGITAL_TV) && + ((delsys != priv->delsys) || bw != priv->bw)) + need_calibration = true; + else + need_calibration = false; + + if (need_calibration) { + tuner_dbg("calibrating the tuner\n"); + for (i = 0; i < 2; i++) { + /* Set filt_cap */ + rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0x60); + if (rc < 0) + return rc; + + /* set cali clk =on */ + rc = r820t_write_reg_mask(priv, 0x0f, 0x04, 0x04); + if (rc < 0) + return rc; + + /* X'tal cap 0pF for PLL */ + rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x03); + if (rc < 0) + return rc; + + rc = r820t_set_pll(priv, filt_cal_lo); + if (rc < 0 || !priv->has_lock) + return rc; + + /* Start Trigger */ + rc = r820t_write_reg_mask(priv, 0x0b, 0x10, 0x10); + if (rc < 0) + return rc; + + msleep(1); + + /* Stop Trigger */ + rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10); + if (rc < 0) + return rc; + + /* set cali clk =off */ + rc = r820t_write_reg_mask(priv, 0x0f, 0x00, 0x04); + if (rc < 0) + return rc; + + /* Check if calibration worked */ + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + priv->fil_cal_code = data[4] & 0x0f; + if (priv->fil_cal_code && priv->fil_cal_code != 0x0f) + break; + } + /* narrowest */ + if (priv->fil_cal_code == 0x0f) + priv->fil_cal_code = 0; + } + + rc = r820t_write_reg_mask(priv, 0x0a, + filt_q | priv->fil_cal_code, 0x1f); + if (rc < 0) + return rc; + + /* Set BW, Filter_gain, & HP corner */ + rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0x10); + if (rc < 0) + return rc; + + + /* Set Img_R */ + rc = r820t_write_reg_mask(priv, 0x07, img_r, 0x80); + if (rc < 0) + return rc; + + /* Set filt_3dB, V6MHz */ + rc = r820t_write_reg_mask(priv, 0x06, filt_gain, 0x30); + if (rc < 0) + return rc; + + /* channel filter extension */ + rc = r820t_write_reg_mask(priv, 0x1e, ext_enable, 0x60); + if (rc < 0) + return rc; + + /* Loop through */ + rc = r820t_write_reg_mask(priv, 0x05, loop_through, 0x80); + if (rc < 0) + return rc; + + /* Loop through attenuation */ + rc = r820t_write_reg_mask(priv, 0x1f, lt_att, 0x80); + if (rc < 0) + return rc; + + /* filter extension widest */ + rc = r820t_write_reg_mask(priv, 0x0f, flt_ext_widest, 0x80); + if (rc < 0) + return rc; + + /* RF poly filter current */ + rc = r820t_write_reg_mask(priv, 0x19, polyfil_cur, 0x60); + if (rc < 0) + return rc; + + /* Store current standard. If it changes, re-calibrate the tuner */ + priv->delsys = delsys; + priv->type = type; + priv->std = std; + priv->bw = bw; + + return 0; +} + +static int generic_set_freq(struct dvb_frontend *fe, + u32 freq /* in HZ */, + unsigned bw, + enum v4l2_tuner_type type, + v4l2_std_id std, u32 delsys) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc = -EINVAL; + u32 lo_freq; + + tuner_dbg("should set frequency to %d kHz, bw %d MHz\n", + freq / 1000, bw); + + mutex_lock(&priv->lock); + + if ((type == V4L2_TUNER_ANALOG_TV) && (std == V4L2_STD_SECAM_LC)) + lo_freq = freq - priv->int_freq; + else + lo_freq = freq + priv->int_freq; + + rc = r820t_set_tv_standard(priv, bw, type, std, delsys); + if (rc < 0) + goto err; + + rc = r820t_set_mux(priv, lo_freq); + if (rc < 0) + goto err; + rc = r820t_set_pll(priv, lo_freq); + if (rc < 0 || !priv->has_lock) + goto err; + + rc = r820t_sysfreq_sel(priv, freq, type, std, delsys); +err: + mutex_unlock(&priv->lock); + + if (rc < 0) + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +/* + * r820t standby logic + */ + +static int r820t_standby(struct r820t_priv *priv) +{ + int rc; + + rc = r820t_write_reg(priv, 0x06, 0xb1); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x05, 0x03); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x07, 0x3a); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x08, 0x40); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x09, 0xc0); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0a, 0x36); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0c, 0x35); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0f, 0x68); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x11, 0x03); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x17, 0xf4); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x19, 0x0c); + + /* Force initial calibration */ + priv->type = -1; + + return rc; +} + +/* + * r820t device init logic + */ + +static int r820t_xtal_check(struct r820t_priv *priv) +{ + int rc, i; + u8 data[3], val; + + /* Initialize the shadow registers */ + memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); + + /* cap 30pF & Drive Low */ + rc = r820t_write_reg_mask(priv, 0x10, 0x0b, 0x0b); + if (rc < 0) + return rc; + + /* set pll autotune = 128kHz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x0c); + if (rc < 0) + return rc; + + /* set manual initial reg = 111111; */ + rc = r820t_write_reg_mask(priv, 0x13, 0x7f, 0x7f); + if (rc < 0) + return rc; + + /* set auto */ + rc = r820t_write_reg_mask(priv, 0x13, 0x00, 0x40); + if (rc < 0) + return rc; + + /* Try several xtal capacitor alternatives */ + for (i = 0; i < ARRAY_SIZE(r820t_xtal_capacitor); i++) { + rc = r820t_write_reg_mask(priv, 0x10, + r820t_xtal_capacitor[i][0], 0x1b); + if (rc < 0) + return rc; + + msleep(5); + + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + if ((!data[2]) & 0x40) + continue; + + val = data[2] & 0x3f; + + if (priv->cfg->xtal == 16000000 && (val > 29 || val < 23)) + break; + + if (val != 0x3f) + break; + } + + if (i == ARRAY_SIZE(r820t_xtal_capacitor)) + return -EINVAL; + + return r820t_xtal_capacitor[i][1]; +} + +/* + * r820t frontend operations and tuner attach code + */ + +static int r820t_init(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc, i; + int xtal_cap = 0; + + tuner_dbg("%s:\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + mutex_lock(&priv->lock); + + if ((priv->cfg->rafael_chip == CHIP_R820T) || + (priv->cfg->rafael_chip == CHIP_R828S) || + (priv->cfg->rafael_chip == CHIP_R820C)) { + priv->xtal_cap_sel = XTAL_HIGH_CAP_0P; + } else { + for (i = 0; i < 3; i++) { + rc = r820t_xtal_check(priv); + if (rc < 0) + goto err; + if (!i || rc > xtal_cap) + xtal_cap = rc; + } + priv->xtal_cap_sel = xtal_cap; + } + + /* Initialize registers */ + rc = r820t_write(priv, 0x05, + r820t_init_array, sizeof(r820t_init_array)); + + mutex_unlock(&priv->lock); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return rc; +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +static int r820t_sleep(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc; + + tuner_dbg("%s:\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + mutex_lock(&priv->lock); + rc = r820t_standby(priv); + mutex_unlock(&priv->lock); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +static int r820t_set_analog_freq(struct dvb_frontend *fe, + struct analog_parameters *p) +{ + struct r820t_priv *priv = fe->tuner_priv; + unsigned bw; + + tuner_dbg("%s called\n", __func__); + + /* if std is not defined, choose one */ + if (!p->std) + p->std = V4L2_STD_MN; + + if ((p->std == V4L2_STD_PAL_M) || (p->std == V4L2_STD_NTSC)) + bw = 6; + else + bw = 8; + + return generic_set_freq(fe, 62500l * p->frequency, bw, + V4L2_TUNER_ANALOG_TV, p->std, SYS_UNDEFINED); +} + +static int r820t_set_params(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; + unsigned bw; + + tuner_dbg("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", + __func__, c->delivery_system, c->frequency, c->bandwidth_hz); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + bw = (c->bandwidth_hz + 500000) / 1000000; + if (!bw) + bw = 8; + + rc = generic_set_freq(fe, c->frequency, bw, + V4L2_TUNER_DIGITAL_TV, 0, c->delivery_system); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (rc) + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +static int r820t_signal(struct dvb_frontend *fe, u16 *strength) +{ + struct r820t_priv *priv = fe->tuner_priv; + + if (priv->has_lock) + *strength = 0xffff; + else + *strength = 0; + + return 0; +} + +static int r820t_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct r820t_priv *priv = fe->tuner_priv; + + tuner_dbg("%s:\n", __func__); + + *frequency = priv->int_freq; + + return 0; +} + +static int r820t_release(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + + tuner_dbg("%s:\n", __func__); + + mutex_lock(&r820t_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&r820t_list_mutex); + + fe->tuner_priv = NULL; + + kfree(fe->tuner_priv); + + return 0; +} + +static const struct dvb_tuner_ops r820t_tuner_ops = { + .info = { + .name = "Rafael Micro R820T", + .frequency_min = 42000000, + .frequency_max = 1002000000, + }, + .init = r820t_init, + .release = r820t_release, + .sleep = r820t_sleep, + .set_params = r820t_set_params, + .set_analog_params = r820t_set_analog_freq, + .get_if_frequency = r820t_get_if_frequency, + .get_rf_strength = r820t_signal, +}; + +struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct r820t_config *cfg) +{ + struct r820t_priv *priv; + int rc = -ENODEV; + u8 data[5]; + int instance; + + mutex_lock(&r820t_list_mutex); + + instance = hybrid_tuner_request_state(struct r820t_priv, priv, + hybrid_tuner_instance_list, + i2c, cfg->i2c_addr, + "r820t"); + switch (instance) { + case 0: + /* memory allocation failure */ + goto err_no_gate; + break; + case 1: + /* new tuner instance */ + priv->cfg = cfg; + + mutex_init(&priv->lock); + + fe->tuner_priv = priv; + break; + case 2: + /* existing tuner instance */ + fe->tuner_priv = priv; + break; + } + + memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops, sizeof(r820t_tuner_ops)); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* check if the tuner is there */ + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + goto err; + + rc = r820t_sleep(fe); + if (rc < 0) + goto err; + + tuner_info("Rafael Micro r820t successfully identified\n"); + + fe->tuner_priv = priv; + memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + mutex_unlock(&r820t_list_mutex); + + return fe; +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + +err_no_gate: + mutex_unlock(&r820t_list_mutex); + + tuner_info("%s: failed=%d\n", __func__, rc); + r820t_release(fe); + return NULL; +} +EXPORT_SYMBOL_GPL(r820t_attach); + +MODULE_DESCRIPTION("Rafael Micro r820t silicon tuner driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/r820t.h b/drivers/media/tuners/r820t.h new file mode 100644 index 000000000000..a64a7b630729 --- /dev/null +++ b/drivers/media/tuners/r820t.h @@ -0,0 +1,55 @@ +/* + * Elonics R820T silicon tuner driver + * + * Copyright (C) 2012 Antti Palosaari + * + * 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 R820T_H +#define R820T_H + +#include +#include "dvb_frontend.h" + +enum r820t_chip { + CHIP_R820T, + CHIP_R828S, + CHIP_R820C, +}; + +struct r820t_config { + u8 i2c_addr; /* 0x34 */ + + u32 xtal; + enum r820t_chip rafael_chip; + unsigned max_i2c_msg_len; +}; + +#if IS_ENABLED(CONFIG_MEDIA_TUNER_R820T) +struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct r820t_config *cfg); +#else +static inline struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct r820t_config *cfg) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif -- cgit v1.2.3 From 6889ab2a25cf39a048edc69b4d61884b8b4da6b0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 6 Apr 2013 09:40:11 -0300 Subject: [media] rtl28xxu: add support for Rafael Micro r820t This tuner is used on some rtl2882 dongles. Add it to the driver. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/usb/dvb-usb-v2/Kconfig | 1 + drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 30 ++++++++++++++++++++++++++++++ drivers/media/usb/dvb-usb-v2/rtl28xxu.h | 1 + 3 files changed, 32 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 9aff03555464..a3c8ecf22078 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -143,6 +143,7 @@ config DVB_USB_RTL28XXU select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_R820T if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Realtek RTL28xxU DVB USB receiver. diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 3d128a5e4794..18756a6d03d5 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -33,6 +33,7 @@ #include "e4000.h" #include "fc2580.h" #include "tua9001.h" +#include "r820t.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -375,6 +376,7 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf}; struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf}; struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf}; + struct rtl28xxu_req req_r820t = {0x0034, CMD_I2C_RD, 5, buf}; dev_dbg(&d->udev->dev, "%s:\n", __func__); @@ -479,6 +481,14 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) goto found; } + /* check R820T by reading tuner stats at I2C addr 0x1a */ + ret = rtl28xxu_ctrl_msg(d, &req_r820t); + if (ret == 0) { + priv->tuner = TUNER_RTL2832_R820T; + priv->tuner_name = "R820T"; + goto found; + } + found: dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name); @@ -589,6 +599,12 @@ static struct rtl2832_config rtl28xxu_rtl2832_e4000_config = { .tuner = TUNER_RTL2832_E4000, }; +static struct rtl2832_config rtl28xxu_rtl2832_r820t_config = { + .i2c_addr = 0x10, + .xtal = 28800000, + .tuner = TUNER_RTL2832_R820T, +}; + static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) { @@ -728,6 +744,9 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) case TUNER_RTL2832_E4000: rtl2832_config = &rtl28xxu_rtl2832_e4000_config; break; + case TUNER_RTL2832_R820T: + rtl2832_config = &rtl28xxu_rtl2832_r820t_config; + break; default: dev_err(&d->udev->dev, "%s: unknown tuner=%s\n", KBUILD_MODNAME, priv->tuner_name); @@ -840,6 +859,13 @@ static const struct fc0012_config rtl2832u_fc0012_config = { .xtal_freq = FC_XTAL_28_8_MHZ, }; +static const struct r820t_config rtl2832u_r820t_config = { + .i2c_addr = 0x1a, + .xtal = 28800000, + .max_i2c_msg_len = 2, + .rafael_chip = CHIP_R820T, +}; + static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -889,6 +915,10 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(tua9001_attach, adap->fe[0], &d->i2c_adap, &rtl2832u_tua9001_config); break; + case TUNER_RTL2832_R820T: + fe = dvb_attach(r820t_attach, adap->fe[0], &d->i2c_adap, + &rtl2832u_r820t_config); + break; default: fe = NULL; dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME, diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h index 2f3af2d3b6ce..533a33127289 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h @@ -82,6 +82,7 @@ enum rtl28xxu_tuner { TUNER_RTL2832_E4000, TUNER_RTL2832_TDA18272, TUNER_RTL2832_FC0013, + TUNER_RTL2832_R820T, }; struct rtl28xxu_req { -- cgit v1.2.3 From f8fde0e045aeac4417b09bef01b3ada74a4cbc80 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 7 Apr 2013 18:12:56 -0300 Subject: [media] r820t: Give a better estimation of the signal strength Instead of a binary signal strength measure, use the tuner gain to obtain a better estimation of the signal strength. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 7e02920f385a..ed9cd6569548 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1082,6 +1082,18 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, return 0; } +static int r820t_read_gain(struct r820t_priv *priv) +{ + u8 data[4]; + int rc; + + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + return ((data[3] & 0x0f) << 1) + ((data[3] & 0xf0) >> 4); +} + static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, unsigned bw, @@ -1353,11 +1365,23 @@ static int r820t_set_params(struct dvb_frontend *fe) static int r820t_signal(struct dvb_frontend *fe, u16 *strength) { struct r820t_priv *priv = fe->tuner_priv; + int rc = 0; - if (priv->has_lock) - *strength = 0xffff; - else + if (priv->has_lock) { + rc = r820t_read_gain(priv); + if (rc < 0) + return rc; + + /* A higher gain at LNA means a lower signal strength */ + *strength = (45 - rc) << 4 | 0xff; + } else { *strength = 0; + } + + tuner_dbg("%s: %s, gain=%d strength=%d\n", + __func__, + priv->has_lock ? "PLL locked" : "no signal", + rc, *strength); return 0; } -- cgit v1.2.3 From 50786ddfa8efef696ec4f72460874f3b46dc2401 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 7 Apr 2013 18:33:13 -0300 Subject: [media] r820t: Set gain mode to auto This tuner works with 2 modes: automatic gain mode and manual gain mode. Put it into automatic mode, as we currently don't have any API for manual gain adjustment. The logic to allow setting the manual mode is there, as it is just a few extra code. This way, if/when we latter add support for setting the gain mode, the code is already there. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 91 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index ed9cd6569548..0fa355d1737c 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -320,6 +320,20 @@ static int r820t_xtal_capacitor[][2] = { { 0x10, XTAL_HIGH_CAP_0P }, }; +/* + * measured with a Racal 6103E GSM test set at 928 MHz with -60 dBm + * input power, for raw results see: + * http://steve-m.de/projects/rtl-sdr/gain_measurement/r820t/ + */ + +static const int r820t_lna_gain_steps[] = { + 0, 9, 13, 40, 38, 13, 31, 22, 26, 31, 26, 14, 19, 5, 35, 13 +}; + +static const int r820t_mixer_gain_steps[] = { + 0, 5, 10, 10, 19, 9, 10, 25, 17, 10, 8, 16, 13, 6, 3, -8 +}; + /* * I2C read/write code and shadow registers logic */ @@ -1094,6 +1108,78 @@ static int r820t_read_gain(struct r820t_priv *priv) return ((data[3] & 0x0f) << 1) + ((data[3] & 0xf0) >> 4); } +static int r820t_set_gain_mode(struct r820t_priv *priv, + bool set_manual_gain, + int gain) +{ + int rc; + + if (set_manual_gain) { + int i, total_gain = 0; + uint8_t mix_index = 0, lna_index = 0; + u8 data[4]; + + /* LNA auto off */ + rc = r820t_write_reg_mask(priv, 0x05, 0x10, 0x10); + if (rc < 0) + return rc; + + /* Mixer auto off */ + rc = r820t_write_reg_mask(priv, 0x07, 0, 0x10); + if (rc < 0) + return rc; + + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + /* set fixed VGA gain for now (16.3 dB) */ + rc = r820t_write_reg_mask(priv, 0x0c, 0x08, 0x9f); + if (rc < 0) + return rc; + + for (i = 0; i < 15; i++) { + if (total_gain >= gain) + break; + + total_gain += r820t_lna_gain_steps[++lna_index]; + + if (total_gain >= gain) + break; + + total_gain += r820t_mixer_gain_steps[++mix_index]; + } + + /* set LNA gain */ + rc = r820t_write_reg_mask(priv, 0x05, lna_index, 0x0f); + if (rc < 0) + return rc; + + /* set Mixer gain */ + rc = r820t_write_reg_mask(priv, 0x07, mix_index, 0x0f); + if (rc < 0) + return rc; + } else { + /* LNA */ + rc = r820t_write_reg_mask(priv, 0x05, 0, 0xef); + if (rc < 0) + return rc; + + /* Mixer */ + rc = r820t_write_reg_mask(priv, 0x07, 0x10, 0xef); + if (rc < 0) + return rc; + + /* set fixed VGA gain for now (26.5 dB) */ + rc = r820t_write_reg_mask(priv, 0x0c, 0x0b, 0x9f); + if (rc < 0) + return rc; + } + + return 0; +} + + static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, unsigned bw, @@ -1121,6 +1207,11 @@ static int generic_set_freq(struct dvb_frontend *fe, rc = r820t_set_mux(priv, lo_freq); if (rc < 0) goto err; + + rc = r820t_set_gain_mode(priv, true, 0); + if (rc < 0) + goto err; + rc = r820t_set_pll(priv, lo_freq); if (rc < 0 || !priv->has_lock) goto err; -- cgit v1.2.3 From 9e30edd8907e5a7e92b04b1d05de5619c4a35d47 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 7 Apr 2013 18:57:15 -0300 Subject: [media] rtl28xxu: use r820t to obtain the signal strength Now that we can get the strength from r820t, use it. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 18756a6d03d5..22015fe1a0f3 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -918,6 +918,10 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) case TUNER_RTL2832_R820T: fe = dvb_attach(r820t_attach, adap->fe[0], &d->i2c_adap, &rtl2832u_r820t_config); + + /* Use tuner to get the signal strength */ + adap->fe[0]->ops.read_signal_strength = + adap->fe[0]->ops.tuner_ops.get_rf_strength; break; default: fe = NULL; -- cgit v1.2.3 From 7a5ef30dc3363ae1ab7243397be7dd8449063999 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 7 Apr 2013 19:32:55 -0300 Subject: [media] r820t: proper lock and set the I2C gate As this tuner can be used by analog and digital parts of the driver, be sure that all ops that access the hardware will be be properly locked. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 50 +++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 0fa355d1737c..5cd8256a203e 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1193,8 +1193,6 @@ static int generic_set_freq(struct dvb_frontend *fe, tuner_dbg("should set frequency to %d kHz, bw %d MHz\n", freq / 1000, bw); - mutex_lock(&priv->lock); - if ((type == V4L2_TUNER_ANALOG_TV) && (std == V4L2_STD_SECAM_LC)) lo_freq = freq - priv->int_freq; else @@ -1218,7 +1216,6 @@ static int generic_set_freq(struct dvb_frontend *fe, rc = r820t_sysfreq_sel(priv, freq, type, std, delsys); err: - mutex_unlock(&priv->lock); if (rc < 0) tuner_dbg("%s: failed=%d\n", __func__, rc); @@ -1335,6 +1332,8 @@ static int r820t_xtal_check(struct r820t_priv *priv) /* * r820t frontend operations and tuner attach code + * + * All driver locks and i2c control are only in this part of the code */ static int r820t_init(struct dvb_frontend *fe) @@ -1345,11 +1344,10 @@ static int r820t_init(struct dvb_frontend *fe) tuner_dbg("%s:\n", __func__); + mutex_lock(&priv->lock); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - mutex_lock(&priv->lock); - if ((priv->cfg->rafael_chip == CHIP_R820T) || (priv->cfg->rafael_chip == CHIP_R828S) || (priv->cfg->rafael_chip == CHIP_R820C)) { @@ -1369,17 +1367,13 @@ static int r820t_init(struct dvb_frontend *fe) rc = r820t_write(priv, 0x05, r820t_init_array, sizeof(r820t_init_array)); - mutex_unlock(&priv->lock); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return rc; err: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); - tuner_dbg("%s: failed=%d\n", __func__, rc); + if (rc < 0) + tuner_dbg("%s: failed=%d\n", __func__, rc); return rc; } @@ -1390,15 +1384,15 @@ static int r820t_sleep(struct dvb_frontend *fe) tuner_dbg("%s:\n", __func__); + mutex_lock(&priv->lock); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - mutex_lock(&priv->lock); rc = r820t_standby(priv); - mutex_unlock(&priv->lock); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); tuner_dbg("%s: failed=%d\n", __func__, rc); return rc; @@ -1409,6 +1403,7 @@ static int r820t_set_analog_freq(struct dvb_frontend *fe, { struct r820t_priv *priv = fe->tuner_priv; unsigned bw; + int rc; tuner_dbg("%s called\n", __func__); @@ -1421,8 +1416,18 @@ static int r820t_set_analog_freq(struct dvb_frontend *fe, else bw = 8; - return generic_set_freq(fe, 62500l * p->frequency, bw, - V4L2_TUNER_ANALOG_TV, p->std, SYS_UNDEFINED); + mutex_lock(&priv->lock); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + rc = generic_set_freq(fe, 62500l * p->frequency, bw, + V4L2_TUNER_ANALOG_TV, p->std, SYS_UNDEFINED); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); + + return rc; } static int r820t_set_params(struct dvb_frontend *fe) @@ -1435,6 +1440,7 @@ static int r820t_set_params(struct dvb_frontend *fe) tuner_dbg("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", __func__, c->delivery_system, c->frequency, c->bandwidth_hz); + mutex_lock(&priv->lock); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -1447,6 +1453,7 @@ static int r820t_set_params(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); if (rc) tuner_dbg("%s: failed=%d\n", __func__, rc); @@ -1458,10 +1465,14 @@ static int r820t_signal(struct dvb_frontend *fe, u16 *strength) struct r820t_priv *priv = fe->tuner_priv; int rc = 0; + mutex_lock(&priv->lock); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (priv->has_lock) { rc = r820t_read_gain(priv); if (rc < 0) - return rc; + goto err; /* A higher gain at LNA means a lower signal strength */ *strength = (45 - rc) << 4 | 0xff; @@ -1469,6 +1480,11 @@ static int r820t_signal(struct dvb_frontend *fe, u16 *strength) *strength = 0; } +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + mutex_unlock(&priv->lock); + tuner_dbg("%s: %s, gain=%d strength=%d\n", __func__, priv->has_lock ? "PLL locked" : "no signal", -- cgit v1.2.3 From da31934fb87dbe907d2ea77f300313f280792d8d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 7 Apr 2013 19:47:49 -0300 Subject: [media] rtl820t: Add a debug msg when PLL gets locked [ 2255.342797] r820t 3-001a: generic_set_freq: PLL locked on frequency 725476191 Hz, gain=45 Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 5cd8256a203e..1821cf192642 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1215,6 +1215,12 @@ static int generic_set_freq(struct dvb_frontend *fe, goto err; rc = r820t_sysfreq_sel(priv, freq, type, std, delsys); + if (rc < 0) + goto err; + + tuner_dbg("%s: PLL locked on frequency %d Hz, gain=%d\n", + __func__, freq, r820t_read_gain(priv)); + err: if (rc < 0) -- cgit v1.2.3 From f60f5bcbe47d1a7c83b278740d22482a49a70269 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 9 Apr 2013 18:46:10 -0300 Subject: [media] r820t: Fix IF scale Scale used at get_if_freq and LO freq calculus is Hz. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 1821cf192642..2ecf1d2ab82f 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -975,7 +975,7 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, return rc; msleep(1); } - priv->int_freq = if_khz; + priv->int_freq = if_khz * 1000; /* Check if standard changed. If so, filter calibration is needed */ if (type != priv->type) -- cgit v1.2.3 From fa4bfd2bd0548c0e5e0d931147df45157686de71 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 9 Apr 2013 18:19:50 -0300 Subject: [media] rtl2832: add code to bind r820t on it There are some init stuff to be done for each new tuner at the demod code. Add the code there for r820t. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/dvb-frontends/rtl2832.c | 76 +++++++++++++++++++++++------- drivers/media/dvb-frontends/rtl2832.h | 1 + drivers/media/dvb-frontends/rtl2832_priv.h | 28 +++++++++++ 3 files changed, 88 insertions(+), 17 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 73887690b046..b6f50c7a15e6 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -432,22 +432,12 @@ static int rtl2832_init(struct dvb_frontend *fe) {DVBT_TR_THD_SET2, 0x6}, {DVBT_TRK_KC_I2, 0x5}, {DVBT_CR_THD_SET2, 0x1}, - {DVBT_SPEC_INV, 0x0}, }; dev_dbg(&priv->i2c->dev, "%s:\n", __func__); en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0); - /* - * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) - * / CrystalFreqHz) - */ - pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal; - pset_iffreq *= 0x400000; - pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); - pset_iffreq = pset_iffreq & 0x3fffff; - for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) { ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg, rtl2832_initial_regs[i].value); @@ -472,6 +462,10 @@ static int rtl2832_init(struct dvb_frontend *fe) len = ARRAY_SIZE(rtl2832_tuner_init_e4000); init = rtl2832_tuner_init_e4000; break; + case RTL2832_TUNER_R820T: + len = ARRAY_SIZE(rtl2832_tuner_init_r820t); + init = rtl2832_tuner_init_r820t; + break; default: ret = -EINVAL; goto err; @@ -483,14 +477,43 @@ static int rtl2832_init(struct dvb_frontend *fe) goto err; } - /* if frequency settings */ - ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); - if (ret) - goto err; + /* + * if frequency settings + * Some tuners (r820t) don't initialize IF here; instead; they do it + * at set_params() + */ + if (!fe->ops.tuner_ops.get_if_frequency) { + /* + * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) + * / CrystalFreqHz) + */ + pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal; + pset_iffreq *= 0x400000; + pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); + pset_iffreq = pset_iffreq & 0x3fffff; + ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); + if (ret) + goto err; + + ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); + if (ret) + goto err; + } - ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); - if (ret) - goto err; + /* + * r820t NIM code does a software reset here at the demod - + * may not be needed, as there's already a software reset at set_params() + */ +#if 1 + /* soft reset */ + ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1); + if (ret) + goto err; + + ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0); + if (ret) + goto err; +#endif priv->sleeping = false; @@ -564,6 +587,25 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) if (fe->ops.tuner_ops.set_params) fe->ops.tuner_ops.set_params(fe); + /* If the frontend has get_if_frequency(), use it */ + if (fe->ops.tuner_ops.get_if_frequency) { + u32 if_freq; + u64 pset_iffreq; + + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); + if (ret) + goto err; + + pset_iffreq = if_freq % priv->cfg.xtal; + pset_iffreq *= 0x400000; + pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); + pset_iffreq = pset_iffreq & 0x3fffff; + + ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); + if (ret) + goto err; + } + switch (c->bandwidth_hz) { case 6000000: i = 0; diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h index fefba0e9ba30..91b2dcf5a6ea 100644 --- a/drivers/media/dvb-frontends/rtl2832.h +++ b/drivers/media/dvb-frontends/rtl2832.h @@ -52,6 +52,7 @@ struct rtl2832_config { #define RTL2832_TUNER_FC0012 0x26 #define RTL2832_TUNER_E4000 0x27 #define RTL2832_TUNER_FC0013 0x29 +#define RTL2832_TUNER_R820T 0x2a u8 tuner; }; diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index 7d97ce9d2193..b5f2b80092ee 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -267,6 +267,7 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_tua9001[] = { {DVBT_OPT_ADC_IQ, 0x1}, {DVBT_AD_AVI, 0x0}, {DVBT_AD_AVQ, 0x0}, + {DVBT_SPEC_INV, 0x0}, }; static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = { @@ -300,6 +301,7 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = { {DVBT_GI_PGA_STATE, 0x0}, {DVBT_EN_AGC_PGA, 0x1}, {DVBT_IF_AGC_MAN, 0x0}, + {DVBT_SPEC_INV, 0x0}, }; static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = { @@ -337,6 +339,32 @@ static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = { {DVBT_REG_MONSEL, 0x1}, {DVBT_REG_MON, 0x1}, {DVBT_REG_4MSEL, 0x0}, + {DVBT_SPEC_INV, 0x0}, +}; + +static const struct rtl2832_reg_value rtl2832_tuner_init_r820t[] = { + {DVBT_DAGC_TRG_VAL, 0x39}, + {DVBT_AGC_TARG_VAL_0, 0x0}, + {DVBT_AGC_TARG_VAL_8_1, 0x40}, + {DVBT_AAGC_LOOP_GAIN, 0x16}, + {DVBT_LOOP_GAIN2_3_0, 0x8}, + {DVBT_LOOP_GAIN2_4, 0x1}, + {DVBT_LOOP_GAIN3, 0x18}, + {DVBT_VTOP1, 0x35}, + {DVBT_VTOP2, 0x21}, + {DVBT_VTOP3, 0x21}, + {DVBT_KRF1, 0x0}, + {DVBT_KRF2, 0x40}, + {DVBT_KRF3, 0x10}, + {DVBT_KRF4, 0x10}, + {DVBT_IF_AGC_MIN, 0x80}, + {DVBT_IF_AGC_MAX, 0x7f}, + {DVBT_RF_AGC_MIN, 0x80}, + {DVBT_RF_AGC_MAX, 0x7f}, + {DVBT_POLAR_RF_AGC, 0x0}, + {DVBT_POLAR_IF_AGC, 0x0}, + {DVBT_AD7_SETTING, 0xe9f4}, + {DVBT_SPEC_INV, 0x1}, }; #endif /* RTL2832_PRIV_H */ -- cgit v1.2.3 From a7dd065f4976b54d0e75d58f8e94066501fede76 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 9 Apr 2013 21:29:40 -0300 Subject: [media] r820t: use the right IF for the selected TV standard IF is set at r820t_set_tv_standard(). So, we can't calculate LO frequency before calling it. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 2ecf1d2ab82f..48ff6bb75f81 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1193,15 +1193,15 @@ static int generic_set_freq(struct dvb_frontend *fe, tuner_dbg("should set frequency to %d kHz, bw %d MHz\n", freq / 1000, bw); + rc = r820t_set_tv_standard(priv, bw, type, std, delsys); + if (rc < 0) + goto err; + if ((type == V4L2_TUNER_ANALOG_TV) && (std == V4L2_STD_SECAM_LC)) lo_freq = freq - priv->int_freq; else lo_freq = freq + priv->int_freq; - rc = r820t_set_tv_standard(priv, bw, type, std, delsys); - if (rc < 0) - goto err; - rc = r820t_set_mux(priv, lo_freq); if (rc < 0) goto err; -- cgit v1.2.3 From 884655ad2e85ebcd84b10b82fca2ef8e431d4392 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 9 Apr 2013 22:16:52 -0300 Subject: [media] rtl2832: properly set en_bbin for r820t DVBT_EN_BBIN should be set on both places where IF is set. So, move it to a function and call it where needed. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/dvb-frontends/rtl2832.c | 63 +++++++++++++++++------------------ 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index b6f50c7a15e6..2f5a2b504932 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -380,13 +380,37 @@ err: return ret; } -static int rtl2832_init(struct dvb_frontend *fe) + +static int rtl2832_set_if(struct dvb_frontend *fe, u32 if_freq) { struct rtl2832_priv *priv = fe->demodulator_priv; - int i, ret, len; - u8 en_bbin; + int ret; u64 pset_iffreq; + u8 en_bbin = (if_freq == 0 ? 0x1 : 0x0); + + /* + * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) + * / CrystalFreqHz) + */ + + pset_iffreq = if_freq % priv->cfg.xtal; + pset_iffreq *= 0x400000; + pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); + pset_iffreq = pset_iffreq & 0x3fffff; + ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); + if (ret) + return ret; + + ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); + + return (ret); +} + +static int rtl2832_init(struct dvb_frontend *fe) +{ + struct rtl2832_priv *priv = fe->demodulator_priv; const struct rtl2832_reg_value *init; + int i, ret, len; /* initialization values for the demodulator registers */ struct rtl2832_reg_value rtl2832_initial_regs[] = { @@ -436,8 +460,6 @@ static int rtl2832_init(struct dvb_frontend *fe) dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0); - for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) { ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg, rtl2832_initial_regs[i].value); @@ -477,27 +499,10 @@ static int rtl2832_init(struct dvb_frontend *fe) goto err; } - /* - * if frequency settings - * Some tuners (r820t) don't initialize IF here; instead; they do it - * at set_params() - */ if (!fe->ops.tuner_ops.get_if_frequency) { - /* - * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) - * / CrystalFreqHz) - */ - pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal; - pset_iffreq *= 0x400000; - pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); - pset_iffreq = pset_iffreq & 0x3fffff; - ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); - if (ret) - goto err; - - ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); - if (ret) - goto err; + ret = rtl2832_set_if(fe, priv->cfg.if_dvbt); + if (ret) + goto err; } /* @@ -590,18 +595,12 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) /* If the frontend has get_if_frequency(), use it */ if (fe->ops.tuner_ops.get_if_frequency) { u32 if_freq; - u64 pset_iffreq; ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); if (ret) goto err; - pset_iffreq = if_freq % priv->cfg.xtal; - pset_iffreq *= 0x400000; - pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); - pset_iffreq = pset_iffreq & 0x3fffff; - - ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); + ret = rtl2832_set_if(fe, if_freq); if (ret) goto err; } -- cgit v1.2.3 From 103fe2fb2314f09d9b923c5919e70cbe66830d6d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 07:08:17 -0300 Subject: [media] r820t: Invert bits for read ops On read, the bit order is inverted. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 48ff6bb75f81..79ab2b74bd52 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -35,8 +35,10 @@ #include #include #include -#include "tuner-i2c.h" +#include #include + +#include "tuner-i2c.h" #include "r820t.h" /* @@ -414,7 +416,7 @@ static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, static int r820_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) { - int rc; + int rc, i; u8 *p = &priv->buf[1]; priv->buf[0] = reg; @@ -431,7 +433,8 @@ static int r820_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) __func__, reg, len, len, p); /* Copy data to the output buffer */ - memcpy(val, p, len); + for (i = 0; i < len; i++) + val[i] = bitrev8(p[i]); return 0; } -- cgit v1.2.3 From 6189f80d5d6ae58b7262a0908cabf7858397711f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 07:33:23 -0300 Subject: [media] r820t: use the second table for 7MHz The Realtek Kernel driver uses the second DVB-T 7MHz table instead of the first one. Use it as well. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 79ab2b74bd52..1880807e3e8d 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -915,6 +915,14 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ polyfil_cur = 0x60; /* r25[6:5]:min */ } else if (bw == 7) { +#if 0 + /* + * There are two 7 MHz tables defined on the original + * driver, but just the second one seems to be visible + * by rtl2832. Keep this one here commented, as it + * might be needed in the future + */ + if_khz = 4070; filt_cal_lo = 60000; filt_gain = 0x10; /* +3db, 6mhz on */ @@ -926,7 +934,8 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, lt_att = 0x00; /* r31[7], lt att enable */ flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ polyfil_cur = 0x60; /* r25[6:5]:min */ -#if 0 /* 7 MHz type 2 - nor sure why/where this is used - Perhaps Australia? */ +#endif + /* 7 MHz, second table */ if_khz = 4570; filt_cal_lo = 63000; filt_gain = 0x10; /* +3db, 6mhz on */ @@ -938,7 +947,6 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, lt_att = 0x00; /* r31[7], lt att enable */ flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ polyfil_cur = 0x60; /* r25[6:5]:min */ -#endif } else { if_khz = 4570; filt_cal_lo = 68500; -- cgit v1.2.3 From 6b8c2308761029868f36d9037377806c63cf06e9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 07:43:10 -0300 Subject: [media] r820t: Show the read data in the bit-reversed order As the driver's logic uses the bit-reversed order for read, use it as well when displaying the debug messages. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 1880807e3e8d..bb154449a2cf 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -429,13 +429,14 @@ static int r820_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) return rc; return -EREMOTEIO; } - tuner_dbg("%s: i2c rd reg=%02x len=%d: %*ph\n", - __func__, reg, len, len, p); /* Copy data to the output buffer */ for (i = 0; i < len; i++) val[i] = bitrev8(p[i]); + tuner_dbg("%s: i2c rd reg=%02x len=%d: %*ph\n", + __func__, reg, len, len, val); + return 0; } -- cgit v1.2.3 From 84ddc33c20cd026871eb3585ed77badacb0fc113 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 08:51:45 -0300 Subject: [media] r820t: add support for diplexer This is part of the original driver, and adding it doesn't hurt, so add it, to better sync the code. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 12 ++++++++++++ drivers/media/tuners/r820t.h | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index bb154449a2cf..5be4635c521d 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -101,6 +101,7 @@ struct r820t_freq_range { }; #define VCO_POWER_REF 0x02 +#define DIP_FREQ 32000000 /* * Static constants @@ -751,6 +752,17 @@ static int r820t_sysfreq_sel(struct r820t_priv *priv, u32 freq, break; } + if (priv->cfg->use_diplexer && + ((priv->cfg->rafael_chip == CHIP_R820T) || + (priv->cfg->rafael_chip == CHIP_R828S) || + (priv->cfg->rafael_chip == CHIP_R820C))) { + if (freq > DIP_FREQ) + air_cable1_in = 0x00; + else + air_cable1_in = 0x60; + cable2_in = 0x00; + } + rc = r820t_write_reg_mask(priv, 0x1d, lna_top, 0xc7); if (rc < 0) return rc; diff --git a/drivers/media/tuners/r820t.h b/drivers/media/tuners/r820t.h index a64a7b630729..949575a41d49 100644 --- a/drivers/media/tuners/r820t.h +++ b/drivers/media/tuners/r820t.h @@ -32,10 +32,10 @@ enum r820t_chip { struct r820t_config { u8 i2c_addr; /* 0x34 */ - u32 xtal; enum r820t_chip rafael_chip; unsigned max_i2c_msg_len; + bool use_diplexer; }; #if IS_ENABLED(CONFIG_MEDIA_TUNER_R820T) -- cgit v1.2.3 From 75c1819e5e42064e66d4f88a5af628a1604ad52d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 09:04:03 -0300 Subject: [media] r820t: better report signal strength If signal is zero, shows it as a zero, not as 0xff. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 5be4635c521d..d5686e8a8bc6 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1506,6 +1506,8 @@ static int r820t_signal(struct dvb_frontend *fe, u16 *strength) /* A higher gain at LNA means a lower signal strength */ *strength = (45 - rc) << 4 | 0xff; + if (*strength == 0xff) + *strength = 0; } else { *strength = 0; } -- cgit v1.2.3 From 8678b03428b6894d146695980f04f26af7b9b3ec Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 10:50:50 -0300 Subject: [media] r820t: split the function that read cached regs As we'll need to retrieve cached registers, make this function explicit. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index d5686e8a8bc6..ef100ab3564d 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -402,15 +402,25 @@ static int r820t_write_reg(struct r820t_priv *priv, u8 reg, u8 val) return r820t_write(priv, reg, &val, 1); } -static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, - u8 bit_mask) +static int r820t_read_cache_reg(struct r820t_priv *priv, int reg) { - int r = reg - REG_SHADOW_START; + reg -= REG_SHADOW_START; - if (r >= 0 && r < NUM_REGS) - val = (priv->regs[r] & ~bit_mask) | (val & bit_mask); + if (reg >= 0 && reg < NUM_REGS) + return priv->regs[reg]; else return -EINVAL; +} + +static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, + u8 bit_mask) +{ + int rc = r820t_read_cache_reg(priv, reg); + + if (rc < 0) + return rc; + + val = (rc & ~bit_mask) | (val & bit_mask); return r820t_write(priv, reg, &val, 1); } -- cgit v1.2.3 From 226471a18f5458d42aac52bca62ebd8edeb37a96 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 10:53:35 -0300 Subject: [media] r820t: fix prefix of the r820t_read() function Just cosmetic changes: all other functions are prefixed by r820t. Do the same for r820t_read(). Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index ef100ab3564d..e9367d896a07 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -425,7 +425,7 @@ static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, return r820t_write(priv, reg, &val, 1); } -static int r820_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) +static int r820t_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) { int rc, i; u8 *p = &priv->buf[1]; @@ -573,7 +573,7 @@ static int r820t_set_pll(struct r820t_priv *priv, u32 freq) mix_div = mix_div << 1; } - rc = r820_read(priv, 0x00, data, sizeof(data)); + rc = r820t_read(priv, 0x00, data, sizeof(data)); if (rc < 0) return rc; @@ -660,7 +660,7 @@ static int r820t_set_pll(struct r820t_priv *priv, u32 freq) msleep(10); /* Check if PLL has locked */ - rc = r820_read(priv, 0x00, data, 3); + rc = r820t_read(priv, 0x00, data, 3); if (rc < 0) return rc; if (data[2] & 0x40) @@ -1062,7 +1062,7 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, return rc; /* Check if calibration worked */ - rc = r820_read(priv, 0x00, data, sizeof(data)); + rc = r820t_read(priv, 0x00, data, sizeof(data)); if (rc < 0) return rc; @@ -1135,7 +1135,7 @@ static int r820t_read_gain(struct r820t_priv *priv) u8 data[4]; int rc; - rc = r820_read(priv, 0x00, data, sizeof(data)); + rc = r820t_read(priv, 0x00, data, sizeof(data)); if (rc < 0) return rc; @@ -1163,7 +1163,7 @@ static int r820t_set_gain_mode(struct r820t_priv *priv, if (rc < 0) return rc; - rc = r820_read(priv, 0x00, data, sizeof(data)); + rc = r820t_read(priv, 0x00, data, sizeof(data)); if (rc < 0) return rc; @@ -1349,7 +1349,7 @@ static int r820t_xtal_check(struct r820t_priv *priv) msleep(5); - rc = r820_read(priv, 0x00, data, sizeof(data)); + rc = r820t_read(priv, 0x00, data, sizeof(data)); if (rc < 0) return rc; if ((!data[2]) & 0x40) @@ -1621,7 +1621,7 @@ struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 1); /* check if the tuner is there */ - rc = r820_read(priv, 0x00, data, sizeof(data)); + rc = r820t_read(priv, 0x00, data, sizeof(data)); if (rc < 0) goto err; -- cgit v1.2.3 From 9cc2570a1dd74c22b4644b325257cf92d4cdb031 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 15:48:02 -0300 Subject: [media] r820t: use usleep_range() Instead of using msleep(), use sleep_range(), as it provides a closer sleep time. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index e9367d896a07..279be4f78e7a 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -657,7 +657,7 @@ static int r820t_set_pll(struct r820t_priv *priv, u32 freq) * FIXME: Rafael chips R620D, R828D and R828 seems to * need 20 ms for analog TV */ - msleep(10); + usleep_range(10000, 11000); /* Check if PLL has locked */ rc = r820t_read(priv, 0x00, data, 3); @@ -1007,7 +1007,7 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, rc = r820t_write_reg_mask(priv, 0x1d, 0x00, 0x38); if (rc < 0) return rc; - msleep(1); + usleep_range(1000, 2000); } priv->int_freq = if_khz * 1000; @@ -1049,7 +1049,7 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, if (rc < 0) return rc; - msleep(1); + usleep_range(1000, 2000); /* Stop Trigger */ rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10); @@ -1347,7 +1347,7 @@ static int r820t_xtal_check(struct r820t_priv *priv) if (rc < 0) return rc; - msleep(5); + usleep_range(5000, 6000); rc = r820t_read(priv, 0x00, data, sizeof(data)); if (rc < 0) -- cgit v1.2.3 From d75d538899da00bdf8f152c65a99eda1ab59daa3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 15:54:46 -0300 Subject: [media] r820t: proper initialize the PLL register The rtl-sdr library, from where this driver was initially based, doesn't use half PLL clock, but this is used on the Realtek Kernel driver. So, also do the same here. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 43 ++++++++++++++++++++++++++++--------------- drivers/media/tuners/r820t.h | 3 +++ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 279be4f78e7a..07d032350c1b 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -522,10 +522,12 @@ static int r820t_set_mux(struct r820t_priv *priv, u32 freq) return rc; } -static int r820t_set_pll(struct r820t_priv *priv, u32 freq) +static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type, + u32 freq) { u64 tmp64, vco_freq; int rc, i; + unsigned sleep_time = 10000; u32 vco_fra; /* VCO contribution by SDM (kHz) */ u32 vco_min = 1770000; u32 vco_max = vco_min * 2; @@ -535,17 +537,34 @@ static int r820t_set_pll(struct r820t_priv *priv, u32 freq) u8 mix_div = 2; u8 div_buf = 0; u8 div_num = 0; + u8 refdiv2 = 0; u8 ni, si, nint, vco_fine_tune, val; u8 data[5]; - freq = freq / 1000; /* Frequency in kHz */ - + /* Frequency in kHz */ + freq = freq / 1000; pll_ref = priv->cfg->xtal / 1000; - tuner_dbg("set r820t pll for frequency %d kHz = %d\n", freq, pll_ref); + if ((priv->cfg->rafael_chip == CHIP_R620D) || + (priv->cfg->rafael_chip == CHIP_R828D) || + (priv->cfg->rafael_chip == CHIP_R828)) { + /* ref set refdiv2, reffreq = Xtal/2 on ATV application */ + if (type != V4L2_TUNER_DIGITAL_TV) { + pll_ref /= 2; + refdiv2 = 0x10; + sleep_time = 20000; + } + } else { + if (priv->cfg->xtal > 24000000) { + pll_ref /= 2; + refdiv2 = 0x10; + } + } - /* FIXME: this seems to be a hack - probably it can be removed */ - rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x00); + tuner_dbg("set r820t pll for frequency %d kHz = %d%s\n", + freq, pll_ref, refdiv2 ? " / 2" : ""); + + rc = r820t_write_reg_mask(priv, 0x10, refdiv2, 0x10); if (rc < 0) return rc; @@ -598,8 +617,6 @@ static int r820t_set_pll(struct r820t_priv *priv, u32 freq) do_div(tmp64, 1000); vco_fra = (u16)(tmp64); - pll_ref /= 1000; - /* boundary spur prevention */ if (vco_fra < pll_ref / 64) { vco_fra = 0; @@ -653,11 +670,7 @@ static int r820t_set_pll(struct r820t_priv *priv, u32 freq) return rc; for (i = 0; i < 2; i++) { - /* - * FIXME: Rafael chips R620D, R828D and R828 seems to - * need 20 ms for analog TV - */ - usleep_range(10000, 11000); + usleep_range(sleep_time, sleep_time + 1000); /* Check if PLL has locked */ rc = r820t_read(priv, 0x00, data, 3); @@ -1040,7 +1053,7 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, if (rc < 0) return rc; - rc = r820t_set_pll(priv, filt_cal_lo); + rc = r820t_set_pll(priv, type, filt_cal_lo); if (rc < 0 || !priv->has_lock) return rc; @@ -1244,7 +1257,7 @@ static int generic_set_freq(struct dvb_frontend *fe, if (rc < 0) goto err; - rc = r820t_set_pll(priv, lo_freq); + rc = r820t_set_pll(priv, type, lo_freq); if (rc < 0 || !priv->has_lock) goto err; diff --git a/drivers/media/tuners/r820t.h b/drivers/media/tuners/r820t.h index 949575a41d49..4c0823b21693 100644 --- a/drivers/media/tuners/r820t.h +++ b/drivers/media/tuners/r820t.h @@ -26,6 +26,9 @@ enum r820t_chip { CHIP_R820T, + CHIP_R620D, + CHIP_R828D, + CHIP_R828, CHIP_R828S, CHIP_R820C, }; -- cgit v1.2.3 From 25cf4d462abd6d78d224b564de82dcdab7973673 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 15:55:48 -0300 Subject: [media] r820t: add IMR calibrate code This code seems to calibrate I/Q phase and gain during the device initialization. This is done only once, and it doesn't seem to be needed to happen after resuming. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 702 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 681 insertions(+), 21 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 07d032350c1b..fa2e9ae48c98 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -47,6 +47,8 @@ */ #define REG_SHADOW_START 5 #define NUM_REGS 27 +#define NUM_IMR 5 +#define IMR_TRIAL 9 #define VER_NUM 49 @@ -66,6 +68,12 @@ enum xtal_cap_value { XTAL_HIGH_CAP_0P }; +struct r820t_sect_type { + u8 phase_y; + u8 gain_x; + u16 value; +}; + struct r820t_priv { struct list_head hybrid_tuner_instance_list; const struct r820t_config *cfg; @@ -80,6 +88,8 @@ struct r820t_priv { u8 fil_cal_code; bool imr_done; + struct r820t_sect_type imr_data[NUM_IMR]; + /* Store current mode */ u32 delsys; enum v4l2_tuner_type type; @@ -459,7 +469,7 @@ static int r820t_set_mux(struct r820t_priv *priv, u32 freq) { const struct r820t_freq_range *range; int i, rc; - u8 val; + u8 val, reg08, reg09; /* Get the proper frequency range */ freq = freq / 1000000; @@ -507,17 +517,18 @@ static int r820t_set_mux(struct r820t_priv *priv, u32 freq) if (rc < 0) return rc; - /* - * FIXME: the original driver has a logic there with preserves - * gain/phase from registers 8 and 9 reading the data from the - * registers before writing, if "IMF done". That code was sort of - * commented there, as the flag is always false. - */ - rc = r820t_write_reg_mask(priv, 0x08, 0, 0x3f); + if (priv->imr_done) { + reg08 = priv->imr_data[range->imr_mem].gain_x; + reg09 = priv->imr_data[range->imr_mem].phase_y; + } else { + reg08 = 0; + reg09 = 0; + } + rc = r820t_write_reg_mask(priv, 0x08, reg08, 0x3f); if (rc < 0) return rc; - rc = r820t_write_reg_mask(priv, 0x09, 0, 0x3f); + rc = r820t_write_reg_mask(priv, 0x09, reg09, 0x3f); return rc; } @@ -1383,24 +1394,621 @@ static int r820t_xtal_check(struct r820t_priv *priv) return r820t_xtal_capacitor[i][1]; } -/* - * r820t frontend operations and tuner attach code - * - * All driver locks and i2c control are only in this part of the code - */ +static int r820t_imr_prepare(struct r820t_priv *priv) +{ + int rc; -static int r820t_init(struct dvb_frontend *fe) + /* Initialize the shadow registers */ + memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); + + /* lna off (air-in off) */ + rc = r820t_write_reg_mask(priv, 0x05, 0x20, 0x20); + if (rc < 0) + return rc; + + /* mixer gain mode = manual */ + rc = r820t_write_reg_mask(priv, 0x07, 0, 0x10); + if (rc < 0) + return rc; + + /* filter corner = lowest */ + rc = r820t_write_reg_mask(priv, 0x0a, 0x0f, 0x0f); + if (rc < 0) + return rc; + + /* filter bw=+2cap, hp=5M */ + rc = r820t_write_reg_mask(priv, 0x0b, 0x60, 0x6f); + if (rc < 0) + return rc; + + /* adc=on, vga code mode, gain = 26.5dB */ + rc = r820t_write_reg_mask(priv, 0x0c, 0x0b, 0x9f); + if (rc < 0) + return rc; + + /* ring clk = on */ + rc = r820t_write_reg_mask(priv, 0x0f, 0, 0x08); + if (rc < 0) + return rc; + + /* ring power = on */ + rc = r820t_write_reg_mask(priv, 0x18, 0x10, 0x10); + if (rc < 0) + return rc; + + /* from ring = ring pll in */ + rc = r820t_write_reg_mask(priv, 0x1c, 0x02, 0x02); + if (rc < 0) + return rc; + + /* sw_pdect = det3 */ + rc = r820t_write_reg_mask(priv, 0x1e, 0x80, 0x80); + if (rc < 0) + return rc; + + /* Set filt_3dB */ + rc = r820t_write_reg_mask(priv, 0x06, 0x20, 0x20); + + return rc; +} + +static int r820t_multi_read(struct r820t_priv *priv) +{ + int rc, i; + u8 data[2], min = 0, max = 255, sum = 0; + + usleep_range(5000, 6000); + + for (i = 0; i < 6; i++) { + rc = r820t_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + sum += data[1]; + + if (data[1] < min) + min = data[1]; + + if (data[1] > max) + max = data[1]; + } + rc = sum - max - min; + + return rc; +} + +static int r820t_imr_cross(struct r820t_priv *priv, + struct r820t_sect_type iq_point[3], + u8 *x_direct) +{ + struct r820t_sect_type cross[5]; /* (0,0)(0,Q-1)(0,I-1)(Q-1,0)(I-1,0) */ + struct r820t_sect_type tmp; + int i, rc; + u8 reg08, reg09; + + reg08 = r820t_read_cache_reg(priv, 8) & 0xc0; + reg09 = r820t_read_cache_reg(priv, 9) & 0xc0; + + tmp.gain_x = 0; + tmp.phase_y = 0; + tmp.value = 255; + + for (i = 0; i < 5; i++) { + switch (i) { + case 0: + cross[i].gain_x = reg08; + cross[i].phase_y = reg09; + break; + case 1: + cross[i].gain_x = reg08; /* 0 */ + cross[i].phase_y = reg09 + 1; /* Q-1 */ + break; + case 2: + cross[i].gain_x = reg08; /* 0 */ + cross[i].phase_y = (reg09 | 0x20) + 1; /* I-1 */ + break; + case 3: + cross[i].gain_x = reg08 + 1; /* Q-1 */ + cross[i].phase_y = reg09; + break; + default: + cross[i].gain_x = (reg08 | 0x20) + 1; /* I-1 */ + cross[i].phase_y = reg09; + } + + rc = r820t_write_reg(priv, 0x08, cross[i].gain_x); + if (rc < 0) + return rc; + + rc = r820t_write_reg(priv, 0x09, cross[i].phase_y); + if (rc < 0) + return rc; + + rc = r820t_multi_read(priv); + if (rc < 0) + return rc; + + cross[i].value = rc; + + if (cross[i].value < tmp.value) + memcpy(&tmp, &cross[i], sizeof(tmp)); + } + + if ((tmp.phase_y & 0x1f) == 1) { /* y-direction */ + *x_direct = 0; + + iq_point[0] = cross[0]; + iq_point[1] = cross[1]; + iq_point[2] = cross[2]; + } else { /* (0,0) or x-direction */ + *x_direct = 1; + + iq_point[0] = cross[0]; + iq_point[1] = cross[3]; + iq_point[2] = cross[4]; + } + return 0; +} + +static void r820t_compre_cor(struct r820t_sect_type iq[3]) +{ + int i; + + for (i = 3; i > 0; i--) { + if (iq[0].value > iq[i - 1].value) + swap(iq[0], iq[i - 1]); + } +} + +static int r820t_compre_step(struct r820t_priv *priv, + struct r820t_sect_type iq[3], u8 reg) +{ + int rc; + struct r820t_sect_type tmp; + + /* + * Purpose: if (Gain<9 or Phase<9), Gain+1 or Phase+1 and compare + * with min value: + * new < min => update to min and continue + * new > min => Exit + */ + + /* min value already saved in iq[0] */ + tmp.phase_y = iq[0].phase_y; + tmp.gain_x = iq[0].gain_x; + + while (((tmp.gain_x & 0x1f) < IMR_TRIAL) && + ((tmp.phase_y & 0x1f) < IMR_TRIAL)) { + if (reg == 0x08) + tmp.gain_x++; + else + tmp.phase_y++; + + rc = r820t_write_reg(priv, 0x08, tmp.gain_x); + if (rc < 0) + return rc; + + rc = r820t_write_reg(priv, 0x09, tmp.phase_y); + if (rc < 0) + return rc; + + rc = r820t_multi_read(priv); + if (rc < 0) + return rc; + tmp.value = rc; + + if (tmp.value <= iq[0].value) { + iq[0].gain_x = tmp.gain_x; + iq[0].phase_y = tmp.phase_y; + iq[0].value = tmp.value; + } else { + return 0; + } + + } + + return 0; +} + +static int r820t_iq_tree(struct r820t_priv *priv, + struct r820t_sect_type iq[3], + u8 fix_val, u8 var_val, u8 fix_reg) +{ + int rc, i; + u8 tmp, var_reg; + + /* + * record IMC results by input gain/phase location then adjust + * gain or phase positive 1 step and negtive 1 step, + * both record results + */ + + if (fix_reg == 0x08) + var_reg = 0x09; + else + var_reg = 0x08; + + for (i = 0; i < 3; i++) { + rc = r820t_write_reg(priv, fix_reg, fix_val); + if (rc < 0) + return rc; + + rc = r820t_write_reg(priv, var_reg, var_val); + if (rc < 0) + return rc; + + rc = r820t_multi_read(priv); + if (rc < 0) + return rc; + iq[i].value = rc; + + if (fix_reg == 0x08) { + iq[i].gain_x = fix_val; + iq[i].phase_y = var_val; + } else { + iq[i].phase_y = fix_val; + iq[i].gain_x = var_val; + } + + if (i == 0) { /* try right-side point */ + var_val++; + } else if (i == 1) { /* try left-side point */ + /* if absolute location is 1, change I/Q direction */ + if ((var_val & 0x1f) < 0x02) { + tmp = 2 - (var_val & 0x1f); + + /* b[5]:I/Q selection. 0:Q-path, 1:I-path */ + if (var_val & 0x20) { + var_val &= 0xc0; + var_val |= tmp; + } else { + var_val |= 0x20 | tmp; + } + } else { + var_val -= 2; + } + } + } + + return 0; +} + +static int r820t_section(struct r820t_priv *priv, + struct r820t_sect_type *iq_point) +{ + int rc; + struct r820t_sect_type compare_iq[3], compare_bet[3]; + + /* Try X-1 column and save min result to compare_bet[0] */ + if (!(iq_point->gain_x & 0x1f)) + compare_iq[0].gain_x = ((iq_point->gain_x) & 0xdf) + 1; /* Q-path, Gain=1 */ + else + compare_iq[0].gain_x = iq_point->gain_x - 1; /* left point */ + compare_iq[0].phase_y = iq_point->phase_y; + + /* y-direction */ + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, 0x08); + if (rc < 0) + return rc; + + r820t_compre_cor(compare_iq); + + compare_bet[0] = compare_iq[0]; + + /* Try X column and save min result to compare_bet[1] */ + compare_iq[0].gain_x = iq_point->gain_x; + compare_iq[0].phase_y = iq_point->phase_y; + + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, 0x08); + if (rc < 0) + return rc; + + r820t_compre_cor(compare_iq); + + compare_bet[1] = compare_iq[0]; + + /* Try X+1 column and save min result to compare_bet[2] */ + if ((iq_point->gain_x & 0x1f) == 0x00) + compare_iq[0].gain_x = ((iq_point->gain_x) | 0x20) + 1; /* I-path, Gain=1 */ + else + compare_iq[0].gain_x = iq_point->gain_x + 1; + compare_iq[0].phase_y = iq_point->phase_y; + + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, 0x08); + if (rc < 0) + return rc; + + r820t_compre_cor(compare_iq); + + compare_bet[2] = compare_iq[0]; + + r820t_compre_cor(compare_bet); + + *iq_point = compare_bet[0]; + + return 0; +} + +static int r820t_vga_adjust(struct r820t_priv *priv) +{ + int rc; + u8 vga_count; + + /* increase vga power to let image significant */ + for (vga_count = 12; vga_count < 16; vga_count++) { + rc = r820t_write_reg_mask(priv, 0x0c, vga_count, 0x0f); + if (rc < 0) + return rc; + + usleep_range(10000, 11000); + + rc = r820t_multi_read(priv); + if (rc < 0) + return rc; + + if (rc > 40 * 4) + break; + } + + return 0; +} + +static int r820t_iq(struct r820t_priv *priv, struct r820t_sect_type *iq_pont) +{ + struct r820t_sect_type compare_iq[3]; + int rc; + u8 x_direction = 0; /* 1:x, 0:y */ + u8 dir_reg, other_reg; + + r820t_vga_adjust(priv); + + rc = r820t_imr_cross(priv, compare_iq, &x_direction); + if (rc < 0) + return rc; + + if (x_direction == 1) { + dir_reg = 0x08; + other_reg = 0x09; + } else { + dir_reg = 0x09; + other_reg = 0x08; + } + + /* compare and find min of 3 points. determine i/q direction */ + r820t_compre_cor(compare_iq); + + /* increase step to find min value of this direction */ + rc = r820t_compre_step(priv, compare_iq, dir_reg); + if (rc < 0) + return rc; + + /* the other direction */ + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, dir_reg); + if (rc < 0) + return rc; + + /* compare and find min of 3 points. determine i/q direction */ + r820t_compre_cor(compare_iq); + + /* increase step to find min value on this direction */ + rc = r820t_compre_step(priv, compare_iq, other_reg); + if (rc < 0) + return rc; + + /* check 3 points again */ + rc = r820t_iq_tree(priv, compare_iq, compare_iq[0].gain_x, + compare_iq[0].phase_y, other_reg); + if (rc < 0) + return rc; + + r820t_compre_cor(compare_iq); + + /* section-9 check */ + rc = r820t_section(priv, compare_iq); + + *iq_pont = compare_iq[0]; + + /* reset gain/phase control setting */ + rc = r820t_write_reg_mask(priv, 0x08, 0, 0x3f); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x09, 0, 0x3f); + + return rc; +} + +static int r820t_f_imr(struct r820t_priv *priv, struct r820t_sect_type *iq_pont) +{ + int rc; + + r820t_vga_adjust(priv); + + /* + * search surrounding points from previous point + * try (x-1), (x), (x+1) columns, and find min IMR result point + */ + rc = r820t_section(priv, iq_pont); + if (rc < 0) + return rc; + + return 0; +} + +static int r820t_imr(struct r820t_priv *priv, unsigned imr_mem, bool im_flag) +{ + struct r820t_sect_type imr_point; + int rc; + u32 ring_vco, ring_freq, ring_ref; + u8 n_ring, n; + int reg18, reg19, reg1f; + + if (priv->cfg->xtal > 24000000) + ring_ref = priv->cfg->xtal / 2; + else + ring_ref = priv->cfg->xtal; + + for (n = 0; n < 16; n++) { + if ((16 + n) * 8 * ring_ref >= 3100000) { + n_ring = n; + break; + } + + /* n_ring not found */ + if (n == 15) + n_ring = n; + } + + reg18 = r820t_read_cache_reg(priv, 0x18); + reg19 = r820t_read_cache_reg(priv, 0x19); + reg1f = r820t_read_cache_reg(priv, 0x1f); + + reg18 &= 0xf0; /* set ring[3:0] */ + reg18 |= n_ring; + + ring_vco = (16 + n_ring) * 8 * ring_ref; + + reg18 &= 0xdf; /* clear ring_se23 */ + reg19 &= 0xfc; /* clear ring_seldiv */ + reg1f &= 0xfc; /* clear ring_att */ + + switch (imr_mem) { + case 0: + ring_freq = ring_vco / 48; + reg18 |= 0x20; /* ring_se23 = 1 */ + reg19 |= 0x03; /* ring_seldiv = 3 */ + reg1f |= 0x02; /* ring_att 10 */ + break; + case 1: + ring_freq = ring_vco / 16; + reg18 |= 0x00; /* ring_se23 = 0 */ + reg19 |= 0x02; /* ring_seldiv = 2 */ + reg1f |= 0x00; /* pw_ring 00 */ + break; + case 2: + ring_freq = ring_vco / 8; + reg18 |= 0x00; /* ring_se23 = 0 */ + reg19 |= 0x01; /* ring_seldiv = 1 */ + reg1f |= 0x03; /* pw_ring 11 */ + break; + case 3: + ring_freq = ring_vco / 6; + reg18 |= 0x20; /* ring_se23 = 1 */ + reg19 |= 0x00; /* ring_seldiv = 0 */ + reg1f |= 0x03; /* pw_ring 11 */ + break; + case 4: + ring_freq = ring_vco / 4; + reg18 |= 0x00; /* ring_se23 = 0 */ + reg19 |= 0x00; /* ring_seldiv = 0 */ + reg1f |= 0x01; /* pw_ring 01 */ + break; + default: + ring_freq = ring_vco / 4; + reg18 |= 0x00; /* ring_se23 = 0 */ + reg19 |= 0x00; /* ring_seldiv = 0 */ + reg1f |= 0x01; /* pw_ring 01 */ + break; + } + + + /* write pw_ring, n_ring, ringdiv2 registers */ + + /* n_ring, ring_se23 */ + rc = r820t_write_reg(priv, 0x18, reg18); + if (rc < 0) + return rc; + + /* ring_sediv */ + rc = r820t_write_reg(priv, 0x19, reg19); + if (rc < 0) + return rc; + + /* pw_ring */ + rc = r820t_write_reg(priv, 0x1f, reg1f); + if (rc < 0) + return rc; + + /* mux input freq ~ rf_in freq */ + rc = r820t_set_mux(priv, (ring_freq - 5300) * 1000); + if (rc < 0) + return rc; + + rc = r820t_set_pll(priv, V4L2_TUNER_DIGITAL_TV, + (ring_freq - 5300) * 1000); + if (!priv->has_lock) + rc = -EINVAL; + if (rc < 0) + return rc; + + if (im_flag) { + rc = r820t_iq(priv, &imr_point); + } else { + imr_point.gain_x = priv->imr_data[3].gain_x; + imr_point.phase_y = priv->imr_data[3].phase_y; + imr_point.value = priv->imr_data[3].value; + + rc = r820t_f_imr(priv, &imr_point); + } + if (rc < 0) + return rc; + + /* save IMR value */ + switch (imr_mem) { + case 0: + priv->imr_data[0].gain_x = imr_point.gain_x; + priv->imr_data[0].phase_y = imr_point.phase_y; + priv->imr_data[0].value = imr_point.value; + break; + case 1: + priv->imr_data[1].gain_x = imr_point.gain_x; + priv->imr_data[1].phase_y = imr_point.phase_y; + priv->imr_data[1].value = imr_point.value; + break; + case 2: + priv->imr_data[2].gain_x = imr_point.gain_x; + priv->imr_data[2].phase_y = imr_point.phase_y; + priv->imr_data[2].value = imr_point.value; + break; + case 3: + priv->imr_data[3].gain_x = imr_point.gain_x; + priv->imr_data[3].phase_y = imr_point.phase_y; + priv->imr_data[3].value = imr_point.value; + break; + case 4: + priv->imr_data[4].gain_x = imr_point.gain_x; + priv->imr_data[4].phase_y = imr_point.phase_y; + priv->imr_data[4].value = imr_point.value; + break; + default: + priv->imr_data[4].gain_x = imr_point.gain_x; + priv->imr_data[4].phase_y = imr_point.phase_y; + priv->imr_data[4].value = imr_point.value; + break; + } + + return 0; +} + +static int r820t_imr_callibrate(struct r820t_priv *priv) { - struct r820t_priv *priv = fe->tuner_priv; int rc, i; int xtal_cap = 0; - tuner_dbg("%s:\n", __func__); + if (priv->imr_done) + return 0; - mutex_lock(&priv->lock); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + /* Initialize registers */ + rc = r820t_write(priv, 0x05, + r820t_init_array, sizeof(r820t_init_array)); + if (rc < 0) + return rc; + /* Detect Xtal capacitance */ if ((priv->cfg->rafael_chip == CHIP_R820T) || (priv->cfg->rafael_chip == CHIP_R828S) || (priv->cfg->rafael_chip == CHIP_R820C)) { @@ -1409,7 +2017,7 @@ static int r820t_init(struct dvb_frontend *fe) for (i = 0; i < 3; i++) { rc = r820t_xtal_check(priv); if (rc < 0) - goto err; + return rc; if (!i || rc > xtal_cap) xtal_cap = rc; } @@ -1419,6 +2027,58 @@ static int r820t_init(struct dvb_frontend *fe) /* Initialize registers */ rc = r820t_write(priv, 0x05, r820t_init_array, sizeof(r820t_init_array)); + if (rc < 0) + return rc; + + rc = r820t_imr_prepare(priv); + if (rc < 0) + return rc; + + rc = r820t_imr(priv, 3, true); + if (rc < 0) + return rc; + rc = r820t_imr(priv, 1, false); + if (rc < 0) + return rc; + rc = r820t_imr(priv, 0, false); + if (rc < 0) + return rc; + rc = r820t_imr(priv, 2, false); + if (rc < 0) + return rc; + rc = r820t_imr(priv, 4, false); + if (rc < 0) + return rc; + + priv->imr_done = true; + + return 0; +} + +/* + * r820t frontend operations and tuner attach code + * + * All driver locks and i2c control are only in this part of the code + */ + +static int r820t_init(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc; + + tuner_dbg("%s:\n", __func__); + + mutex_lock(&priv->lock); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + rc = r820t_imr_callibrate(priv); + if (rc < 0) + goto err; + + /* Initialize registers */ + rc = r820t_write(priv, 0x05, + r820t_init_array, sizeof(r820t_init_array)); err: if (fe->ops.i2c_gate_ctrl) -- cgit v1.2.3 From 6596e88043e2c55d934f43701c3ca9860f009b06 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 10 Apr 2013 18:35:17 -0300 Subject: [media] r820t: add a commented code for GPIO Add the code to set the GPIO for this tuner. This code is currently unused, so it is kept there only for completeness. With this patch there are just two things that got left from the original driver: - At standby, there's another mode, not used by rtl2832u. Not sure if it might be needed in the future, but I suspect it is not used at all; - There is a "fast tune" mode. As nor DVB or V4L API supports it, it seems an overkill to implement it. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index fa2e9ae48c98..0125de897c8d 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -2055,6 +2055,14 @@ static int r820t_imr_callibrate(struct r820t_priv *priv) return 0; } +#if 0 +/* Not used, for now */ +static int r820t_gpio(struct r820t_priv *priv, bool enable) +{ + return r820t_write_reg_mask(priv, 0x0f, enable ? 1 : 0, 0x01); +} +#endif + /* * r820t frontend operations and tuner attach code * -- cgit v1.2.3 From 52775fd5b599c79e9a4152f75a6426bf376198dc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 11 Apr 2013 10:59:12 -0300 Subject: [media] r820t: Allow disabling IMR callibration The rtl-sdr library disabled IMR callibration. While I'm not sure yet why, it could be a good idea to add a modprobe parameter here, to allow to also disable it. There are two rationale behind it: - It helps to compare USB dumps between rtl-sdr and the Kernel module; - If rtl-sdr disabled it, perhaps there's a good reason (e. g. it might not be actually working, or it might be causing some trouble). For both cases, it seems useful to add a modprobe parameter to allow testing the device with both configurations. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 0125de897c8d..2e6a690ffb1a 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -56,6 +56,11 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); +static int no_imr_cal; +module_param(no_imr_cal, int, 0444); +MODULE_PARM_DESC(no_imr_cal, "Disable IMR calibration at module init"); + + /* * enums and structures */ @@ -87,7 +92,8 @@ struct r820t_priv { u32 int_freq; u8 fil_cal_code; bool imr_done; - + bool has_lock; + bool init_done; struct r820t_sect_type imr_data[NUM_IMR]; /* Store current mode */ @@ -95,8 +101,6 @@ struct r820t_priv { enum v4l2_tuner_type type; v4l2_std_id std; u32 bw; /* in MHz */ - - bool has_lock; }; struct r820t_freq_range { @@ -1999,7 +2003,7 @@ static int r820t_imr_callibrate(struct r820t_priv *priv) int rc, i; int xtal_cap = 0; - if (priv->imr_done) + if (priv->init_done) return 0; /* Initialize registers */ @@ -2024,6 +2028,16 @@ static int r820t_imr_callibrate(struct r820t_priv *priv) priv->xtal_cap_sel = xtal_cap; } + /* + * Disables IMR callibration. That emulates the same behaviour + * as what is done by rtl-sdr userspace library. Useful for testing + */ + if (no_imr_cal) { + priv->init_done = true; + + return 0; + } + /* Initialize registers */ rc = r820t_write(priv, 0x05, r820t_init_array, sizeof(r820t_init_array)); @@ -2050,6 +2064,7 @@ static int r820t_imr_callibrate(struct r820t_priv *priv) if (rc < 0) return rc; + priv->init_done = true; priv->imr_done = true; return 0; -- cgit v1.2.3 From 4176e7ea8885a3cb3f5bbbf28480b421ef9b8f47 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 11 Apr 2013 13:22:21 -0300 Subject: [media] r820t: avoid rewrite all regs when not needed Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 2e6a690ffb1a..fc660f27edb3 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -2006,18 +2006,17 @@ static int r820t_imr_callibrate(struct r820t_priv *priv) if (priv->init_done) return 0; - /* Initialize registers */ - rc = r820t_write(priv, 0x05, - r820t_init_array, sizeof(r820t_init_array)); - if (rc < 0) - return rc; - /* Detect Xtal capacitance */ if ((priv->cfg->rafael_chip == CHIP_R820T) || (priv->cfg->rafael_chip == CHIP_R828S) || (priv->cfg->rafael_chip == CHIP_R820C)) { priv->xtal_cap_sel = XTAL_HIGH_CAP_0P; } else { + /* Initialize registers */ + rc = r820t_write(priv, 0x05, + r820t_init_array, sizeof(r820t_init_array)); + if (rc < 0) + return rc; for (i = 0; i < 3; i++) { rc = r820t_xtal_check(priv); if (rc < 0) -- cgit v1.2.3 From 064fd169c508ce20e35babdd82946dab7219ccc5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 11 Apr 2013 13:25:10 -0300 Subject: [media] r820t: Don't put it in standby if not initialized yet r820t_standby() can be called before r820t_init(). If that happens, just do nothing. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index fc660f27edb3..b679a3f1fb7a 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1298,6 +1298,10 @@ static int r820t_standby(struct r820t_priv *priv) { int rc; + /* If device was not initialized yet, don't need to standby */ + if (!priv->init_done) + return 0; + rc = r820t_write_reg(priv, 0x06, 0xb1); if (rc < 0) return rc; -- cgit v1.2.3 From c0c6ed8d0479d47949e2345510b8fc8af41f7ed2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 11 Apr 2013 15:04:54 -0300 Subject: [media] r820t: fix PLL calculus There are a few errors at the PLL calculus, causing the device to use wrong values. While here, change the calculus to use 32 bits, as there's no need for 64 bits there. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index b679a3f1fb7a..f5a5fb03dfd5 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "tuner-i2c.h" #include "r820t.h" @@ -540,7 +539,7 @@ static int r820t_set_mux(struct r820t_priv *priv, u32 freq) static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type, u32 freq) { - u64 tmp64, vco_freq; + u32 vco_freq; int rc, i; unsigned sleep_time = 10000; u32 vco_fra; /* VCO contribution by SDM (kHz) */ @@ -576,9 +575,6 @@ static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type, } } - tuner_dbg("set r820t pll for frequency %d kHz = %d%s\n", - freq, pll_ref, refdiv2 ? " / 2" : ""); - rc = r820t_write_reg_mask(priv, 0x10, refdiv2, 0x10); if (rc < 0) return rc; @@ -622,15 +618,9 @@ static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type, if (rc < 0) return rc; - vco_freq = (u64)(freq * (u64)mix_div); - - tmp64 = vco_freq; - do_div(tmp64, 2 * pll_ref); - nint = (u8)tmp64; - - tmp64 = vco_freq - ((u64)2) * pll_ref * nint; - do_div(tmp64, 1000); - vco_fra = (u16)(tmp64); + vco_freq = freq * mix_div; + nint = vco_freq / (2 * pll_ref); + vco_fra = vco_freq - 2 * pll_ref * nint; /* boundary spur prevention */ if (vco_fra < pll_ref / 64) { @@ -677,10 +667,13 @@ static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type, n_sdm = n_sdm << 1; } - rc = r820t_write_reg_mask(priv, 0x16, sdm >> 8, 0x08); + tuner_dbg("freq %d kHz, pll ref %d%s, sdm=0x%04x\n", + freq, pll_ref, refdiv2 ? " / 2" : "", sdm); + + rc = r820t_write_reg(priv, 0x16, sdm >> 8); if (rc < 0) return rc; - rc = r820t_write_reg_mask(priv, 0x15, sdm & 0xff, 0x08); + rc = r820t_write_reg(priv, 0x15, sdm & 0xff); if (rc < 0) return rc; @@ -1068,7 +1061,7 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, if (rc < 0) return rc; - rc = r820t_set_pll(priv, type, filt_cal_lo); + rc = r820t_set_pll(priv, type, filt_cal_lo * 1000); if (rc < 0 || !priv->has_lock) return rc; -- cgit v1.2.3 From f26588947b613925f11f5336320ff4d11b617a62 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 11 Apr 2013 15:47:53 -0300 Subject: [media] r820t: Fix hp_cor filter mask The bit mask was inverted here. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index f5a5fb03dfd5..c644e90a5907 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1102,7 +1102,7 @@ static int r820t_set_tv_standard(struct r820t_priv *priv, return rc; /* Set BW, Filter_gain, & HP corner */ - rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0x10); + rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0xef); if (rc < 0) return rc; -- cgit v1.2.3 From b5e2b97b0ba474dd2abd672be320d1f1030800e8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 11 Apr 2013 15:55:27 -0300 Subject: [media] r820t: put it into automatic gain mode Currently, it is putting it on manual mode. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index c644e90a5907..e63ee9443fa6 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1216,12 +1216,12 @@ static int r820t_set_gain_mode(struct r820t_priv *priv, return rc; } else { /* LNA */ - rc = r820t_write_reg_mask(priv, 0x05, 0, 0xef); + rc = r820t_write_reg_mask(priv, 0x05, 0, 0x10); if (rc < 0) return rc; /* Mixer */ - rc = r820t_write_reg_mask(priv, 0x07, 0x10, 0xef); + rc = r820t_write_reg_mask(priv, 0x07, 0x10, 0x10); if (rc < 0) return rc; @@ -1261,7 +1261,7 @@ static int generic_set_freq(struct dvb_frontend *fe, if (rc < 0) goto err; - rc = r820t_set_gain_mode(priv, true, 0); + rc = r820t_set_gain_mode(priv, false, 0); if (rc < 0) goto err; -- cgit v1.2.3 From c8832e8f4293b1c9fce92a92e2506572f5b11775 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 15 Apr 2013 19:44:39 -0300 Subject: [media] rtl2832: Fix IF calculus Spectrum is inverted. So, we need to invert it when calculating the value for the IF register Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/dvb-frontends/rtl2832.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 2f5a2b504932..facb84841518 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -396,7 +396,11 @@ static int rtl2832_set_if(struct dvb_frontend *fe, u32 if_freq) pset_iffreq = if_freq % priv->cfg.xtal; pset_iffreq *= 0x400000; pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); + pset_iffreq = -pset_iffreq; pset_iffreq = pset_iffreq & 0x3fffff; + dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d pset_iffreq=%08x\n", + __func__, if_freq, (unsigned)pset_iffreq); + ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); if (ret) return ret; -- cgit v1.2.3 From dfdeac8108db5066e1adaf112adbe396ef27f0bc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 11 Apr 2013 16:20:53 -0300 Subject: [media] r820t: disable auto gain/VGA setting On field tests, the auto gain routine is not working, nor it is used by the original driver. Let's comment it for now. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index e63ee9443fa6..8d9977919d96 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1163,6 +1163,8 @@ static int r820t_read_gain(struct r820t_priv *priv) return ((data[3] & 0x0f) << 1) + ((data[3] & 0xf0) >> 4); } +#if 0 +/* FIXME: This routine requires more testing */ static int r820t_set_gain_mode(struct r820t_priv *priv, bool set_manual_gain, int gain) @@ -1233,7 +1235,7 @@ static int r820t_set_gain_mode(struct r820t_priv *priv, return 0; } - +#endif static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, @@ -1261,10 +1263,6 @@ static int generic_set_freq(struct dvb_frontend *fe, if (rc < 0) goto err; - rc = r820t_set_gain_mode(priv, false, 0); - if (rc < 0) - goto err; - rc = r820t_set_pll(priv, type, lo_freq); if (rc < 0 || !priv->has_lock) goto err; -- cgit v1.2.3 From 396f3659aa9decdb0acf82a36e59c20d656e19ed Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 11 Apr 2013 14:27:04 -0300 Subject: [media] r820t: Don't divide the IF by two The original Win driver doesn't do; rtl-sdr also disabled that piece of the code. Signed-off-by: Mauro Carvalho Chehab Tested-by: Antti Palosaari --- drivers/media/tuners/r820t.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 8d9977919d96..905a10615e52 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -559,6 +559,8 @@ static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type, freq = freq / 1000; pll_ref = priv->cfg->xtal / 1000; +#if 0 + /* Doesn't exist on rtl-sdk, and on field tests, caused troubles */ if ((priv->cfg->rafael_chip == CHIP_R620D) || (priv->cfg->rafael_chip == CHIP_R828D) || (priv->cfg->rafael_chip == CHIP_R828)) { @@ -574,6 +576,7 @@ static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type, refdiv2 = 0x10; } } +#endif rc = r820t_write_reg_mask(priv, 0x10, refdiv2, 0x10); if (rc < 0) -- cgit v1.2.3