From 9d3f5535f1ad127ffb1f3a1dd367b43e30bd72b6 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Tue, 5 May 2015 21:41:06 +0300 Subject: [PATCH] linux: add patch to support Hauppauge HVR-955Q ATSC USB receiver This patch adds support for the Hauppauge HVR-955Q ATSC USB receiver, which is a newer version of the HVR-950Q and one of the few ATSC receivers currently being sold on the market and having Linux support. The patch looks rather large, but bulk of the patch is the new demodulator driver for the LGDT3306a demodulator. Obsolete after kernel 4.1 is taken into use. --- .../4.0.1/linux-229-hauppauge-hvr-955q.patch | 2706 +++++++++++++++++ projects/Generic/linux/linux.x86_64.conf | 1 + projects/RPi/linux/linux.arm.conf | 1 + projects/RPi2/linux/linux.arm.conf | 1 + 4 files changed, 2709 insertions(+) create mode 100644 packages/linux/patches/4.0.1/linux-229-hauppauge-hvr-955q.patch diff --git a/packages/linux/patches/4.0.1/linux-229-hauppauge-hvr-955q.patch b/packages/linux/patches/4.0.1/linux-229-hauppauge-hvr-955q.patch new file mode 100644 index 0000000000..d9312c76be --- /dev/null +++ b/packages/linux/patches/4.0.1/linux-229-hauppauge-hvr-955q.patch @@ -0,0 +1,2706 @@ +diff -urN a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig +--- a/drivers/media/dvb-frontends/Kconfig 2015-05-05 20:31:16.696408429 +0300 ++++ b/drivers/media/dvb-frontends/Kconfig 2015-05-05 21:24:21.988519906 +0300 +@@ -604,6 +604,14 @@ + An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want + to support this frontend. + ++config DVB_LGDT3306A ++ tristate "LG Electronics LGDT3306A based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM-B 64/256 demodulator module. Say Y when you want ++ to support this frontend. ++ + config DVB_LG2160 + tristate "LG Electronics LG216x based" + depends on DVB_CORE && I2C +diff -urN a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c +--- a/drivers/media/dvb-frontends/lgdt3306a.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/drivers/media/dvb-frontends/lgdt3306a.c 2015-05-05 21:22:52.244516766 +0300 +@@ -0,0 +1,2144 @@ ++/* ++ * Support for LGDT3306A - 8VSB/QAM-B ++ * ++ * Copyright (C) 2013 Fred Richter ++ * - driver structure based on lgdt3305.[ch] by Michael Krufky ++ * - code based on LG3306_V0.35 API by LG Electronics Inc. ++ * ++ * 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. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include "dvb_math.h" ++#include "lgdt3306a.h" ++ ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); ++ ++#define DBG_INFO 1 ++#define DBG_REG 2 ++#define DBG_DUMP 4 /* FGR - comment out to remove dump code */ ++ ++#define lg_debug(fmt, arg...) \ ++ printk(KERN_DEBUG pr_fmt(fmt), ## arg) ++ ++#define dbg_info(fmt, arg...) \ ++ do { \ ++ if (debug & DBG_INFO) \ ++ lg_debug(fmt, ## arg); \ ++ } while (0) ++ ++#define dbg_reg(fmt, arg...) \ ++ do { \ ++ if (debug & DBG_REG) \ ++ lg_debug(fmt, ## arg); \ ++ } while (0) ++ ++#define lg_chkerr(ret) \ ++({ \ ++ int __ret; \ ++ __ret = (ret < 0); \ ++ if (__ret) \ ++ pr_err("error %d on line %d\n", ret, __LINE__); \ ++ __ret; \ ++}) ++ ++struct lgdt3306a_state { ++ struct i2c_adapter *i2c_adap; ++ const struct lgdt3306a_config *cfg; ++ ++ struct dvb_frontend frontend; ++ ++ fe_modulation_t current_modulation; ++ u32 current_frequency; ++ u32 snr; ++}; ++ ++/* ++ * LG3306A Register Usage ++ * (LG does not really name the registers, so this code does not either) ++ * ++ * 0000 -> 00FF Common control and status ++ * 1000 -> 10FF Synchronizer control and status ++ * 1F00 -> 1FFF Smart Antenna control and status ++ * 2100 -> 21FF VSB Equalizer control and status ++ * 2800 -> 28FF QAM Equalizer control and status ++ * 3000 -> 30FF FEC control and status ++ */ ++ ++enum lgdt3306a_lock_status { ++ LG3306_UNLOCK = 0x00, ++ LG3306_LOCK = 0x01, ++ LG3306_UNKNOWN_LOCK = 0xff ++}; ++ ++enum lgdt3306a_neverlock_status { ++ LG3306_NL_INIT = 0x00, ++ LG3306_NL_PROCESS = 0x01, ++ LG3306_NL_LOCK = 0x02, ++ LG3306_NL_FAIL = 0x03, ++ LG3306_NL_UNKNOWN = 0xff ++}; ++ ++enum lgdt3306a_modulation { ++ LG3306_VSB = 0x00, ++ LG3306_QAM64 = 0x01, ++ LG3306_QAM256 = 0x02, ++ LG3306_UNKNOWN_MODE = 0xff ++}; ++ ++enum lgdt3306a_lock_check { ++ LG3306_SYNC_LOCK, ++ LG3306_FEC_LOCK, ++ LG3306_TR_LOCK, ++ LG3306_AGC_LOCK, ++}; ++ ++ ++#ifdef DBG_DUMP ++static void lgdt3306a_DumpAllRegs(struct lgdt3306a_state *state); ++static void lgdt3306a_DumpRegs(struct lgdt3306a_state *state); ++#endif ++ ++ ++static int lgdt3306a_write_reg(struct lgdt3306a_state *state, u16 reg, u8 val) ++{ ++ int ret; ++ u8 buf[] = { reg >> 8, reg & 0xff, val }; ++ struct i2c_msg msg = { ++ .addr = state->cfg->i2c_addr, .flags = 0, ++ .buf = buf, .len = 3, ++ }; ++ ++ dbg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); ++ ++ ret = i2c_transfer(state->i2c_adap, &msg, 1); ++ ++ if (ret != 1) { ++ pr_err("error (addr %02x %02x <- %02x, err = %i)\n", ++ msg.buf[0], msg.buf[1], msg.buf[2], ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int lgdt3306a_read_reg(struct lgdt3306a_state *state, u16 reg, u8 *val) ++{ ++ int ret; ++ u8 reg_buf[] = { reg >> 8, reg & 0xff }; ++ struct i2c_msg msg[] = { ++ { .addr = state->cfg->i2c_addr, ++ .flags = 0, .buf = reg_buf, .len = 2 }, ++ { .addr = state->cfg->i2c_addr, ++ .flags = I2C_M_RD, .buf = val, .len = 1 }, ++ }; ++ ++ ret = i2c_transfer(state->i2c_adap, msg, 2); ++ ++ if (ret != 2) { ++ pr_err("error (addr %02x reg %04x error (ret == %i)\n", ++ state->cfg->i2c_addr, reg, ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ dbg_reg("reg: 0x%04x, val: 0x%02x\n", reg, *val); ++ ++ return 0; ++} ++ ++#define read_reg(state, reg) \ ++({ \ ++ u8 __val; \ ++ int ret = lgdt3306a_read_reg(state, reg, &__val); \ ++ if (lg_chkerr(ret)) \ ++ __val = 0; \ ++ __val; \ ++}) ++ ++static int lgdt3306a_set_reg_bit(struct lgdt3306a_state *state, ++ u16 reg, int bit, int onoff) ++{ ++ u8 val; ++ int ret; ++ ++ dbg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); ++ ++ ret = lgdt3306a_read_reg(state, reg, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ val &= ~(1 << bit); ++ val |= (onoff & 1) << bit; ++ ++ ret = lgdt3306a_write_reg(state, reg, val); ++ lg_chkerr(ret); ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3306a_soft_reset(struct lgdt3306a_state *state) ++{ ++ int ret; ++ ++ dbg_info("\n"); ++ ++ ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ msleep(20); ++ ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 1); ++ lg_chkerr(ret); ++ ++fail: ++ return ret; ++} ++ ++static int lgdt3306a_mpeg_mode(struct lgdt3306a_state *state, ++ enum lgdt3306a_mpeg_mode mode) ++{ ++ u8 val; ++ int ret; ++ ++ dbg_info("(%d)\n", mode); ++ /* transport packet format - TPSENB=0x80 */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0071, 7, ++ mode == LGDT3306A_MPEG_PARALLEL ? 1 : 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* ++ * start of packet signal duration ++ * TPSSOPBITEN=0x40; 0=byte duration, 1=bit duration ++ */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0071, 6, 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_read_reg(state, 0x0070, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ val |= 0x10; /* TPCLKSUPB=0x10 */ ++ ++ if (mode == LGDT3306A_MPEG_PARALLEL) ++ val &= ~0x10; ++ ++ ret = lgdt3306a_write_reg(state, 0x0070, val); ++ lg_chkerr(ret); ++ ++fail: ++ return ret; ++} ++ ++static int lgdt3306a_mpeg_mode_polarity(struct lgdt3306a_state *state, ++ enum lgdt3306a_tp_clock_edge edge, ++ enum lgdt3306a_tp_valid_polarity valid) ++{ ++ u8 val; ++ int ret; ++ ++ dbg_info("edge=%d, valid=%d\n", edge, valid); ++ ++ ret = lgdt3306a_read_reg(state, 0x0070, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ val &= ~0x06; /* TPCLKPOL=0x04, TPVALPOL=0x02 */ ++ ++ if (edge == LGDT3306A_TPCLK_RISING_EDGE) ++ val |= 0x04; ++ if (valid == LGDT3306A_TP_VALID_HIGH) ++ val |= 0x02; ++ ++ ret = lgdt3306a_write_reg(state, 0x0070, val); ++ lg_chkerr(ret); ++ ++fail: ++ return ret; ++} ++ ++static int lgdt3306a_mpeg_tristate(struct lgdt3306a_state *state, ++ int mode) ++{ ++ u8 val; ++ int ret; ++ ++ dbg_info("(%d)\n", mode); ++ ++ if (mode) { ++ ret = lgdt3306a_read_reg(state, 0x0070, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ /* ++ * Tristate bus; TPOUTEN=0x80, TPCLKOUTEN=0x20, ++ * TPDATAOUTEN=0x08 ++ */ ++ val &= ~0xa8; ++ ret = lgdt3306a_write_reg(state, 0x0070, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* AGCIFOUTENB=0x40; 1=Disable IFAGC pin */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0003, 6, 1); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ } else { ++ /* enable IFAGC pin */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0003, 6, 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_read_reg(state, 0x0070, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ val |= 0xa8; /* enable bus */ ++ ret = lgdt3306a_write_reg(state, 0x0070, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ } ++ ++fail: ++ return ret; ++} ++ ++static int lgdt3306a_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ ++ dbg_info("acquire=%d\n", acquire); ++ ++ return lgdt3306a_mpeg_tristate(state, acquire ? 0 : 1); ++ ++} ++ ++static int lgdt3306a_power(struct lgdt3306a_state *state, ++ int mode) ++{ ++ int ret; ++ ++ dbg_info("(%d)\n", mode); ++ ++ if (mode == 0) { ++ /* into reset */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* power down */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0000, 0, 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ } else { ++ /* out of reset */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0000, 7, 1); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* power up */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0000, 0, 1); ++ if (lg_chkerr(ret)) ++ goto fail; ++ } ++ ++#ifdef DBG_DUMP ++ lgdt3306a_DumpAllRegs(state); ++#endif ++fail: ++ return ret; ++} ++ ++ ++static int lgdt3306a_set_vsb(struct lgdt3306a_state *state) ++{ ++ u8 val; ++ int ret; ++ ++ dbg_info("\n"); ++ ++ /* 0. Spectrum inversion detection manual; spectrum inverted */ ++ ret = lgdt3306a_read_reg(state, 0x0002, &val); ++ val &= 0xf7; /* SPECINVAUTO Off */ ++ val |= 0x04; /* SPECINV On */ ++ ret = lgdt3306a_write_reg(state, 0x0002, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */ ++ ret = lgdt3306a_write_reg(state, 0x0008, 0x80); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 2. Bandwidth mode for VSB(6MHz) */ ++ ret = lgdt3306a_read_reg(state, 0x0009, &val); ++ val &= 0xe3; ++ val |= 0x0c; /* STDOPDETTMODE[2:0]=3 */ ++ ret = lgdt3306a_write_reg(state, 0x0009, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 3. QAM mode detection mode(None) */ ++ ret = lgdt3306a_read_reg(state, 0x0009, &val); ++ val &= 0xfc; /* STDOPDETCMODE[1:0]=0 */ ++ ret = lgdt3306a_write_reg(state, 0x0009, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 4. ADC sampling frequency rate(2x sampling) */ ++ ret = lgdt3306a_read_reg(state, 0x000d, &val); ++ val &= 0xbf; /* SAMPLING4XFEN=0 */ ++ ret = lgdt3306a_write_reg(state, 0x000d, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++#if 0 ++ /* FGR - disable any AICC filtering, testing only */ ++ ++ ret = lgdt3306a_write_reg(state, 0x0024, 0x00); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* AICCFIXFREQ0 NT N-1(Video rejection) */ ++ ret = lgdt3306a_write_reg(state, 0x002e, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x002f, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x0030, 0x00); ++ ++ /* AICCFIXFREQ1 NT N-1(Audio rejection) */ ++ ret = lgdt3306a_write_reg(state, 0x002b, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x002c, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x002d, 0x00); ++ ++ /* AICCFIXFREQ2 NT Co-Channel(Video rejection) */ ++ ret = lgdt3306a_write_reg(state, 0x0028, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x0029, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x002a, 0x00); ++ ++ /* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */ ++ ret = lgdt3306a_write_reg(state, 0x0025, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x0026, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x0027, 0x00); ++ ++#else ++ /* FGR - this works well for HVR-1955,1975 */ ++ ++ /* 5. AICCOPMODE NT N-1 Adj. */ ++ ret = lgdt3306a_write_reg(state, 0x0024, 0x5A); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* AICCFIXFREQ0 NT N-1(Video rejection) */ ++ ret = lgdt3306a_write_reg(state, 0x002e, 0x5A); ++ ret = lgdt3306a_write_reg(state, 0x002f, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x0030, 0x00); ++ ++ /* AICCFIXFREQ1 NT N-1(Audio rejection) */ ++ ret = lgdt3306a_write_reg(state, 0x002b, 0x36); ++ ret = lgdt3306a_write_reg(state, 0x002c, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x002d, 0x00); ++ ++ /* AICCFIXFREQ2 NT Co-Channel(Video rejection) */ ++ ret = lgdt3306a_write_reg(state, 0x0028, 0x2A); ++ ret = lgdt3306a_write_reg(state, 0x0029, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x002a, 0x00); ++ ++ /* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */ ++ ret = lgdt3306a_write_reg(state, 0x0025, 0x06); ++ ret = lgdt3306a_write_reg(state, 0x0026, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x0027, 0x00); ++#endif ++ ++ ret = lgdt3306a_read_reg(state, 0x001e, &val); ++ val &= 0x0f; ++ val |= 0xa0; ++ ret = lgdt3306a_write_reg(state, 0x001e, val); ++ ++ ret = lgdt3306a_write_reg(state, 0x0022, 0x08); ++ ++ ret = lgdt3306a_write_reg(state, 0x0023, 0xFF); ++ ++ ret = lgdt3306a_read_reg(state, 0x211f, &val); ++ val &= 0xef; ++ ret = lgdt3306a_write_reg(state, 0x211f, val); ++ ++ ret = lgdt3306a_write_reg(state, 0x2173, 0x01); ++ ++ ret = lgdt3306a_read_reg(state, 0x1061, &val); ++ val &= 0xf8; ++ val |= 0x04; ++ ret = lgdt3306a_write_reg(state, 0x1061, val); ++ ++ ret = lgdt3306a_read_reg(state, 0x103d, &val); ++ val &= 0xcf; ++ ret = lgdt3306a_write_reg(state, 0x103d, val); ++ ++ ret = lgdt3306a_write_reg(state, 0x2122, 0x40); ++ ++ ret = lgdt3306a_read_reg(state, 0x2141, &val); ++ val &= 0x3f; ++ ret = lgdt3306a_write_reg(state, 0x2141, val); ++ ++ ret = lgdt3306a_read_reg(state, 0x2135, &val); ++ val &= 0x0f; ++ val |= 0x70; ++ ret = lgdt3306a_write_reg(state, 0x2135, val); ++ ++ ret = lgdt3306a_read_reg(state, 0x0003, &val); ++ val &= 0xf7; ++ ret = lgdt3306a_write_reg(state, 0x0003, val); ++ ++ ret = lgdt3306a_read_reg(state, 0x001c, &val); ++ val &= 0x7f; ++ ret = lgdt3306a_write_reg(state, 0x001c, val); ++ ++ /* 6. EQ step size */ ++ ret = lgdt3306a_read_reg(state, 0x2179, &val); ++ val &= 0xf8; ++ ret = lgdt3306a_write_reg(state, 0x2179, val); ++ ++ ret = lgdt3306a_read_reg(state, 0x217a, &val); ++ val &= 0xf8; ++ ret = lgdt3306a_write_reg(state, 0x217a, val); ++ ++ /* 7. Reset */ ++ ret = lgdt3306a_soft_reset(state); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ dbg_info("complete\n"); ++fail: ++ return ret; ++} ++ ++static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation) ++{ ++ u8 val; ++ int ret; ++ ++ dbg_info("modulation=%d\n", modulation); ++ ++ /* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */ ++ ret = lgdt3306a_write_reg(state, 0x0008, 0x08); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 1a. Spectrum inversion detection to Auto */ ++ ret = lgdt3306a_read_reg(state, 0x0002, &val); ++ val &= 0xfb; /* SPECINV Off */ ++ val |= 0x08; /* SPECINVAUTO On */ ++ ret = lgdt3306a_write_reg(state, 0x0002, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 2. Bandwidth mode for QAM */ ++ ret = lgdt3306a_read_reg(state, 0x0009, &val); ++ val &= 0xe3; /* STDOPDETTMODE[2:0]=0 VSB Off */ ++ ret = lgdt3306a_write_reg(state, 0x0009, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 3. : 64QAM/256QAM detection(manual, auto) */ ++ ret = lgdt3306a_read_reg(state, 0x0009, &val); ++ val &= 0xfc; ++ val |= 0x02; /* STDOPDETCMODE[1:0]=1=Manual 2=Auto */ ++ ret = lgdt3306a_write_reg(state, 0x0009, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 3a. : 64QAM/256QAM selection for manual */ ++ ret = lgdt3306a_read_reg(state, 0x101a, &val); ++ val &= 0xf8; ++ if (modulation == QAM_64) ++ val |= 0x02; /* QMDQMODE[2:0]=2=QAM64 */ ++ else ++ val |= 0x04; /* QMDQMODE[2:0]=4=QAM256 */ ++ ++ ret = lgdt3306a_write_reg(state, 0x101a, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 4. ADC sampling frequency rate(4x sampling) */ ++ ret = lgdt3306a_read_reg(state, 0x000d, &val); ++ val &= 0xbf; ++ val |= 0x40; /* SAMPLING4XFEN=1 */ ++ ret = lgdt3306a_write_reg(state, 0x000d, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 5. No AICC operation in QAM mode */ ++ ret = lgdt3306a_read_reg(state, 0x0024, &val); ++ val &= 0x00; ++ ret = lgdt3306a_write_reg(state, 0x0024, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 6. Reset */ ++ ret = lgdt3306a_soft_reset(state); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ dbg_info("complete\n"); ++fail: ++ return ret; ++} ++ ++static int lgdt3306a_set_modulation(struct lgdt3306a_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ int ret; ++ ++ dbg_info("\n"); ++ ++ switch (p->modulation) { ++ case VSB_8: ++ ret = lgdt3306a_set_vsb(state); ++ break; ++ case QAM_64: ++ ret = lgdt3306a_set_qam(state, QAM_64); ++ break; ++ case QAM_256: ++ ret = lgdt3306a_set_qam(state, QAM_256); ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ state->current_modulation = p->modulation; ++ ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3306a_agc_setup(struct lgdt3306a_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ /* TODO: anything we want to do here??? */ ++ dbg_info("\n"); ++ ++ switch (p->modulation) { ++ case VSB_8: ++ break; ++ case QAM_64: ++ case QAM_256: ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3306a_set_inversion(struct lgdt3306a_state *state, ++ int inversion) ++{ ++ int ret; ++ ++ dbg_info("(%d)\n", inversion); ++ ++ ret = lgdt3306a_set_reg_bit(state, 0x0002, 2, inversion ? 1 : 0); ++ return ret; ++} ++ ++static int lgdt3306a_set_inversion_auto(struct lgdt3306a_state *state, ++ int enabled) ++{ ++ int ret; ++ ++ dbg_info("(%d)\n", enabled); ++ ++ /* 0=Manual 1=Auto(QAM only) - SPECINVAUTO=0x04 */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0002, 3, enabled); ++ return ret; ++} ++ ++static int lgdt3306a_spectral_inversion(struct lgdt3306a_state *state, ++ struct dtv_frontend_properties *p, ++ int inversion) ++{ ++ int ret = 0; ++ ++ dbg_info("(%d)\n", inversion); ++#if 0 ++ /* ++ * FGR - spectral_inversion defaults already set for VSB and QAM; ++ * can enable later if desired ++ */ ++ ++ ret = lgdt3306a_set_inversion(state, inversion); ++ ++ switch (p->modulation) { ++ case VSB_8: ++ /* Manual only for VSB */ ++ ret = lgdt3306a_set_inversion_auto(state, 0); ++ break; ++ case QAM_64: ++ case QAM_256: ++ /* Auto ok for QAM */ ++ ret = lgdt3306a_set_inversion_auto(state, 1); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++#endif ++ return ret; ++} ++ ++static int lgdt3306a_set_if(struct lgdt3306a_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ int ret; ++ u16 if_freq_khz; ++ u8 nco1, nco2; ++ ++ switch (p->modulation) { ++ case VSB_8: ++ if_freq_khz = state->cfg->vsb_if_khz; ++ break; ++ case QAM_64: ++ case QAM_256: ++ if_freq_khz = state->cfg->qam_if_khz; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (if_freq_khz) { ++ default: ++ pr_warn("IF=%d KHz is not supportted, 3250 assumed\n", ++ if_freq_khz); ++ /* fallthrough */ ++ case 3250: /* 3.25Mhz */ ++ nco1 = 0x34; ++ nco2 = 0x00; ++ break; ++ case 3500: /* 3.50Mhz */ ++ nco1 = 0x38; ++ nco2 = 0x00; ++ break; ++ case 4000: /* 4.00Mhz */ ++ nco1 = 0x40; ++ nco2 = 0x00; ++ break; ++ case 5000: /* 5.00Mhz */ ++ nco1 = 0x50; ++ nco2 = 0x00; ++ break; ++ case 5380: /* 5.38Mhz */ ++ nco1 = 0x56; ++ nco2 = 0x14; ++ break; ++ } ++ ret = lgdt3306a_write_reg(state, 0x0010, nco1); ++ if (ret) ++ return ret; ++ ret = lgdt3306a_write_reg(state, 0x0011, nco2); ++ if (ret) ++ return ret; ++ ++ dbg_info("if_freq=%d KHz->[%04x]\n", if_freq_khz, nco1<<8 | nco2); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3306a_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ ++ if (state->cfg->deny_i2c_rptr) { ++ dbg_info("deny_i2c_rptr=%d\n", state->cfg->deny_i2c_rptr); ++ return 0; ++ } ++ dbg_info("(%d)\n", enable); ++ ++ /* NI2CRPTEN=0x80 */ ++ return lgdt3306a_set_reg_bit(state, 0x0002, 7, enable ? 0 : 1); ++} ++ ++static int lgdt3306a_sleep(struct lgdt3306a_state *state) ++{ ++ int ret; ++ ++ dbg_info("\n"); ++ state->current_frequency = -1; /* force re-tune, when we wake */ ++ ++ ret = lgdt3306a_mpeg_tristate(state, 1); /* disable data bus */ ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_power(state, 0); /* power down */ ++ lg_chkerr(ret); ++ ++fail: ++ return 0; ++} ++ ++static int lgdt3306a_fe_sleep(struct dvb_frontend *fe) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ ++ return lgdt3306a_sleep(state); ++} ++ ++static int lgdt3306a_init(struct dvb_frontend *fe) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ u8 val; ++ int ret; ++ ++ dbg_info("\n"); ++ ++ /* 1. Normal operation mode */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0001, 0, 1); /* SIMFASTENB=0x01 */ ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 2. Spectrum inversion auto detection (Not valid for VSB) */ ++ ret = lgdt3306a_set_inversion_auto(state, 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 3. Spectrum inversion(According to the tuner configuration) */ ++ ret = lgdt3306a_set_inversion(state, 1); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 4. Peak-to-peak voltage of ADC input signal */ ++ ++ /* ADCSEL1V=0x80=1Vpp; 0x00=2Vpp */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0004, 7, 1); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 5. ADC output data capture clock phase */ ++ ++ /* 0=same phase as ADC clock */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0004, 2, 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 5a. ADC sampling clock source */ ++ ++ /* ADCCLKPLLSEL=0x08; 0=use ext clock, not PLL */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0004, 3, 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 6. Automatic PLL set */ ++ ++ /* PLLSETAUTO=0x40; 0=off */ ++ ret = lgdt3306a_set_reg_bit(state, 0x0005, 6, 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ if (state->cfg->xtalMHz == 24) { /* 24MHz */ ++ /* 7. Frequency for PLL output(0x2564 for 192MHz for 24MHz) */ ++ ret = lgdt3306a_read_reg(state, 0x0005, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ val &= 0xc0; ++ val |= 0x25; ++ ret = lgdt3306a_write_reg(state, 0x0005, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ret = lgdt3306a_write_reg(state, 0x0006, 0x64); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 8. ADC sampling frequency(0x180000 for 24MHz sampling) */ ++ ret = lgdt3306a_read_reg(state, 0x000d, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ val &= 0xc0; ++ val |= 0x18; ++ ret = lgdt3306a_write_reg(state, 0x000d, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ } else if (state->cfg->xtalMHz == 25) { /* 25MHz */ ++ /* 7. Frequency for PLL output */ ++ ret = lgdt3306a_read_reg(state, 0x0005, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ val &= 0xc0; ++ val |= 0x25; ++ ret = lgdt3306a_write_reg(state, 0x0005, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ret = lgdt3306a_write_reg(state, 0x0006, 0x64); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ /* 8. ADC sampling frequency(0x190000 for 25MHz sampling) */ ++ ret = lgdt3306a_read_reg(state, 0x000d, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ val &= 0xc0; ++ val |= 0x19; ++ ret = lgdt3306a_write_reg(state, 0x000d, val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ } else { ++ pr_err("Bad xtalMHz=%d\n", state->cfg->xtalMHz); ++ } ++#if 0 ++ ret = lgdt3306a_write_reg(state, 0x000e, 0x00); ++ ret = lgdt3306a_write_reg(state, 0x000f, 0x00); ++#endif ++ ++ /* 9. Center frequency of input signal of ADC */ ++ ret = lgdt3306a_write_reg(state, 0x0010, 0x34); /* 3.25MHz */ ++ ret = lgdt3306a_write_reg(state, 0x0011, 0x00); ++ ++ /* 10. Fixed gain error value */ ++ ret = lgdt3306a_write_reg(state, 0x0014, 0); /* gain error=0 */ ++ ++ /* 10a. VSB TR BW gear shift initial step */ ++ ret = lgdt3306a_read_reg(state, 0x103c, &val); ++ val &= 0x0f; ++ val |= 0x20; /* SAMGSAUTOSTL_V[3:0] = 2 */ ++ ret = lgdt3306a_write_reg(state, 0x103c, val); ++ ++ /* 10b. Timing offset calibration in low temperature for VSB */ ++ ret = lgdt3306a_read_reg(state, 0x103d, &val); ++ val &= 0xfc; ++ val |= 0x03; ++ ret = lgdt3306a_write_reg(state, 0x103d, val); ++ ++ /* 10c. Timing offset calibration in low temperature for QAM */ ++ ret = lgdt3306a_read_reg(state, 0x1036, &val); ++ val &= 0xf0; ++ val |= 0x0c; ++ ret = lgdt3306a_write_reg(state, 0x1036, val); ++ ++ /* 11. Using the imaginary part of CIR in CIR loading */ ++ ret = lgdt3306a_read_reg(state, 0x211f, &val); ++ val &= 0xef; /* do not use imaginary of CIR */ ++ ret = lgdt3306a_write_reg(state, 0x211f, val); ++ ++ /* 12. Control of no signal detector function */ ++ ret = lgdt3306a_read_reg(state, 0x2849, &val); ++ val &= 0xef; /* NOUSENOSIGDET=0, enable no signal detector */ ++ ret = lgdt3306a_write_reg(state, 0x2849, val); ++ ++ /* FGR - put demod in some known mode */ ++ ret = lgdt3306a_set_vsb(state); ++ ++ /* 13. TP stream format */ ++ ret = lgdt3306a_mpeg_mode(state, state->cfg->mpeg_mode); ++ ++ /* 14. disable output buses */ ++ ret = lgdt3306a_mpeg_tristate(state, 1); ++ ++ /* 15. Sleep (in reset) */ ++ ret = lgdt3306a_sleep(state); ++ lg_chkerr(ret); ++ ++fail: ++ return ret; ++} ++ ++static int lgdt3306a_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ int ret; ++ ++ dbg_info("(%d, %d)\n", p->frequency, p->modulation); ++ ++ if (state->current_frequency == p->frequency && ++ state->current_modulation == p->modulation) { ++ dbg_info(" (already set, skipping ...)\n"); ++ return 0; ++ } ++ state->current_frequency = -1; ++ state->current_modulation = -1; ++ ++ ret = lgdt3306a_power(state, 1); /* power up */ ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ ret = fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++#if 0 ++ if (lg_chkerr(ret)) ++ goto fail; ++ state->current_frequency = p->frequency; ++#endif ++ } ++ ++ ret = lgdt3306a_set_modulation(state, p); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_agc_setup(state, p); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_set_if(state, p); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_spectral_inversion(state, p, ++ state->cfg->spectral_inversion ? 1 : 0); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_mpeg_mode(state, state->cfg->mpeg_mode); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_mpeg_mode_polarity(state, ++ state->cfg->tpclk_edge, ++ state->cfg->tpvalid_polarity); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_mpeg_tristate(state, 0); /* enable data bus */ ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ ret = lgdt3306a_soft_reset(state); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++#ifdef DBG_DUMP ++ lgdt3306a_DumpAllRegs(state); ++#endif ++ state->current_frequency = p->frequency; ++fail: ++ return ret; ++} ++ ++static int lgdt3306a_get_frontend(struct dvb_frontend *fe) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ ++ dbg_info("(%u, %d)\n", ++ state->current_frequency, state->current_modulation); ++ ++ p->modulation = state->current_modulation; ++ p->frequency = state->current_frequency; ++ return 0; ++} ++ ++static enum dvbfe_algo lgdt3306a_get_frontend_algo(struct dvb_frontend *fe) ++{ ++#if 1 ++ return DVBFE_ALGO_CUSTOM; ++#else ++ return DVBFE_ALGO_HW; ++#endif ++} ++ ++/* ------------------------------------------------------------------------ */ ++static int lgdt3306a_monitor_vsb(struct lgdt3306a_state *state) ++{ ++ u8 val; ++ int ret; ++ u8 snrRef, maxPowerMan, nCombDet; ++ u16 fbDlyCir; ++ ++ ret = lgdt3306a_read_reg(state, 0x21a1, &val); ++ if (ret) ++ return ret; ++ snrRef = val & 0x3f; ++ ++ ret = lgdt3306a_read_reg(state, 0x2185, &maxPowerMan); ++ if (ret) ++ return ret; ++ ++ ret = lgdt3306a_read_reg(state, 0x2191, &val); ++ if (ret) ++ return ret; ++ nCombDet = (val & 0x80) >> 7; ++ ++ ret = lgdt3306a_read_reg(state, 0x2180, &val); ++ if (ret) ++ return ret; ++ fbDlyCir = (val & 0x03) << 8; ++ ++ ret = lgdt3306a_read_reg(state, 0x2181, &val); ++ if (ret) ++ return ret; ++ fbDlyCir |= val; ++ ++ dbg_info("snrRef=%d maxPowerMan=0x%x nCombDet=%d fbDlyCir=0x%x\n", ++ snrRef, maxPowerMan, nCombDet, fbDlyCir); ++ ++ /* Carrier offset sub loop bandwidth */ ++ ret = lgdt3306a_read_reg(state, 0x1061, &val); ++ if (ret) ++ return ret; ++ val &= 0xf8; ++ if ((snrRef > 18) && (maxPowerMan > 0x68) ++ && (nCombDet == 0x01) ++ && ((fbDlyCir == 0x03FF) || (fbDlyCir < 0x6C))) { ++ /* SNR is over 18dB and no ghosting */ ++ val |= 0x00; /* final bandwidth = 0 */ ++ } else { ++ val |= 0x04; /* final bandwidth = 4 */ ++ } ++ ret = lgdt3306a_write_reg(state, 0x1061, val); ++ if (ret) ++ return ret; ++ ++ /* Adjust Notch Filter */ ++ ret = lgdt3306a_read_reg(state, 0x0024, &val); ++ if (ret) ++ return ret; ++ val &= 0x0f; ++ if (nCombDet == 0) { /* Turn on the Notch Filter */ ++ val |= 0x50; ++ } ++ ret = lgdt3306a_write_reg(state, 0x0024, val); ++ if (ret) ++ return ret; ++ ++ /* VSB Timing Recovery output normalization */ ++ ret = lgdt3306a_read_reg(state, 0x103d, &val); ++ if (ret) ++ return ret; ++ val &= 0xcf; ++ val |= 0x20; ++ ret = lgdt3306a_write_reg(state, 0x103d, val); ++ ++ return ret; ++} ++ ++static enum lgdt3306a_modulation ++lgdt3306a_check_oper_mode(struct lgdt3306a_state *state) ++{ ++ u8 val = 0; ++ int ret; ++ ++ ret = lgdt3306a_read_reg(state, 0x0081, &val); ++ if (ret) ++ goto err; ++ ++ if (val & 0x80) { ++ dbg_info("VSB\n"); ++ return LG3306_VSB; ++ } ++ if (val & 0x08) { ++ ret = lgdt3306a_read_reg(state, 0x00a6, &val); ++ if (ret) ++ goto err; ++ val = val >> 2; ++ if (val & 0x01) { ++ dbg_info("QAM256\n"); ++ return LG3306_QAM256; ++ } ++ dbg_info("QAM64\n"); ++ return LG3306_QAM64; ++ } ++err: ++ pr_warn("UNKNOWN\n"); ++ return LG3306_UNKNOWN_MODE; ++} ++ ++static enum lgdt3306a_lock_status ++lgdt3306a_check_lock_status(struct lgdt3306a_state *state, ++ enum lgdt3306a_lock_check whatLock) ++{ ++ u8 val = 0; ++ int ret; ++ enum lgdt3306a_modulation modeOper; ++ enum lgdt3306a_lock_status lockStatus; ++ ++ modeOper = LG3306_UNKNOWN_MODE; ++ ++ switch (whatLock) { ++ case LG3306_SYNC_LOCK: ++ { ++ ret = lgdt3306a_read_reg(state, 0x00a6, &val); ++ if (ret) ++ return ret; ++ ++ if ((val & 0x80) == 0x80) ++ lockStatus = LG3306_LOCK; ++ else ++ lockStatus = LG3306_UNLOCK; ++ ++ dbg_info("SYNC_LOCK=%x\n", lockStatus); ++ break; ++ } ++ case LG3306_AGC_LOCK: ++ { ++ ret = lgdt3306a_read_reg(state, 0x0080, &val); ++ if (ret) ++ return ret; ++ ++ if ((val & 0x40) == 0x40) ++ lockStatus = LG3306_LOCK; ++ else ++ lockStatus = LG3306_UNLOCK; ++ ++ dbg_info("AGC_LOCK=%x\n", lockStatus); ++ break; ++ } ++ case LG3306_TR_LOCK: ++ { ++ modeOper = lgdt3306a_check_oper_mode(state); ++ if ((modeOper == LG3306_QAM64) || (modeOper == LG3306_QAM256)) { ++ ret = lgdt3306a_read_reg(state, 0x1094, &val); ++ if (ret) ++ return ret; ++ ++ if ((val & 0x80) == 0x80) ++ lockStatus = LG3306_LOCK; ++ else ++ lockStatus = LG3306_UNLOCK; ++ } else ++ lockStatus = LG3306_UNKNOWN_LOCK; ++ ++ dbg_info("TR_LOCK=%x\n", lockStatus); ++ break; ++ } ++ case LG3306_FEC_LOCK: ++ { ++ modeOper = lgdt3306a_check_oper_mode(state); ++ if ((modeOper == LG3306_QAM64) || (modeOper == LG3306_QAM256)) { ++ ret = lgdt3306a_read_reg(state, 0x0080, &val); ++ if (ret) ++ return ret; ++ ++ if ((val & 0x10) == 0x10) ++ lockStatus = LG3306_LOCK; ++ else ++ lockStatus = LG3306_UNLOCK; ++ } else ++ lockStatus = LG3306_UNKNOWN_LOCK; ++ ++ dbg_info("FEC_LOCK=%x\n", lockStatus); ++ break; ++ } ++ ++ default: ++ lockStatus = LG3306_UNKNOWN_LOCK; ++ pr_warn("UNKNOWN whatLock=%d\n", whatLock); ++ break; ++ } ++ ++ return lockStatus; ++} ++ ++static enum lgdt3306a_neverlock_status ++lgdt3306a_check_neverlock_status(struct lgdt3306a_state *state) ++{ ++ u8 val = 0; ++ int ret; ++ enum lgdt3306a_neverlock_status lockStatus; ++ ++ ret = lgdt3306a_read_reg(state, 0x0080, &val); ++ if (ret) ++ return ret; ++ lockStatus = (enum lgdt3306a_neverlock_status)(val & 0x03); ++ ++ dbg_info("NeverLock=%d", lockStatus); ++ ++ return lockStatus; ++} ++ ++static int lgdt3306a_pre_monitoring(struct lgdt3306a_state *state) ++{ ++ u8 val = 0; ++ int ret; ++ u8 currChDiffACQ, snrRef, mainStrong, aiccrejStatus; ++ ++ /* Channel variation */ ++ ret = lgdt3306a_read_reg(state, 0x21bc, &currChDiffACQ); ++ if (ret) ++ return ret; ++ ++ /* SNR of Frame sync */ ++ ret = lgdt3306a_read_reg(state, 0x21a1, &val); ++ if (ret) ++ return ret; ++ snrRef = val & 0x3f; ++ ++ /* Strong Main CIR */ ++ ret = lgdt3306a_read_reg(state, 0x2199, &val); ++ if (ret) ++ return ret; ++ mainStrong = (val & 0x40) >> 6; ++ ++ ret = lgdt3306a_read_reg(state, 0x0090, &val); ++ if (ret) ++ return ret; ++ aiccrejStatus = (val & 0xf0) >> 4; ++ ++ dbg_info("snrRef=%d mainStrong=%d aiccrejStatus=%d currChDiffACQ=0x%x\n", ++ snrRef, mainStrong, aiccrejStatus, currChDiffACQ); ++ ++#if 0 ++ /* Dynamic ghost exists */ ++ if ((mainStrong == 0) && (currChDiffACQ > 0x70)) ++#endif ++ if (mainStrong == 0) { ++ ret = lgdt3306a_read_reg(state, 0x2135, &val); ++ if (ret) ++ return ret; ++ val &= 0x0f; ++ val |= 0xa0; ++ ret = lgdt3306a_write_reg(state, 0x2135, val); ++ if (ret) ++ return ret; ++ ++ ret = lgdt3306a_read_reg(state, 0x2141, &val); ++ if (ret) ++ return ret; ++ val &= 0x3f; ++ val |= 0x80; ++ ret = lgdt3306a_write_reg(state, 0x2141, val); ++ if (ret) ++ return ret; ++ ++ ret = lgdt3306a_write_reg(state, 0x2122, 0x70); ++ if (ret) ++ return ret; ++ } else { /* Weak ghost or static channel */ ++ ret = lgdt3306a_read_reg(state, 0x2135, &val); ++ if (ret) ++ return ret; ++ val &= 0x0f; ++ val |= 0x70; ++ ret = lgdt3306a_write_reg(state, 0x2135, val); ++ if (ret) ++ return ret; ++ ++ ret = lgdt3306a_read_reg(state, 0x2141, &val); ++ if (ret) ++ return ret; ++ val &= 0x3f; ++ val |= 0x40; ++ ret = lgdt3306a_write_reg(state, 0x2141, val); ++ if (ret) ++ return ret; ++ ++ ret = lgdt3306a_write_reg(state, 0x2122, 0x40); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++static enum lgdt3306a_lock_status ++lgdt3306a_sync_lock_poll(struct lgdt3306a_state *state) ++{ ++ enum lgdt3306a_lock_status syncLockStatus = LG3306_UNLOCK; ++ int i; ++ ++ for (i = 0; i < 2; i++) { ++ msleep(30); ++ ++ syncLockStatus = lgdt3306a_check_lock_status(state, ++ LG3306_SYNC_LOCK); ++ ++ if (syncLockStatus == LG3306_LOCK) { ++ dbg_info("locked(%d)\n", i); ++ return LG3306_LOCK; ++ } ++ } ++ dbg_info("not locked\n"); ++ return LG3306_UNLOCK; ++} ++ ++static enum lgdt3306a_lock_status ++lgdt3306a_fec_lock_poll(struct lgdt3306a_state *state) ++{ ++ enum lgdt3306a_lock_status FECLockStatus = LG3306_UNLOCK; ++ int i; ++ ++ for (i = 0; i < 2; i++) { ++ msleep(30); ++ ++ FECLockStatus = lgdt3306a_check_lock_status(state, ++ LG3306_FEC_LOCK); ++ ++ if (FECLockStatus == LG3306_LOCK) { ++ dbg_info("locked(%d)\n", i); ++ return FECLockStatus; ++ } ++ } ++ dbg_info("not locked\n"); ++ return FECLockStatus; ++} ++ ++static enum lgdt3306a_neverlock_status ++lgdt3306a_neverlock_poll(struct lgdt3306a_state *state) ++{ ++ enum lgdt3306a_neverlock_status NLLockStatus = LG3306_NL_FAIL; ++ int i; ++ ++ for (i = 0; i < 5; i++) { ++ msleep(30); ++ ++ NLLockStatus = lgdt3306a_check_neverlock_status(state); ++ ++ if (NLLockStatus == LG3306_NL_LOCK) { ++ dbg_info("NL_LOCK(%d)\n", i); ++ return NLLockStatus; ++ } ++ } ++ dbg_info("NLLockStatus=%d\n", NLLockStatus); ++ return NLLockStatus; ++} ++ ++static u8 lgdt3306a_get_packet_error(struct lgdt3306a_state *state) ++{ ++ u8 val; ++ int ret; ++ ++ ret = lgdt3306a_read_reg(state, 0x00fa, &val); ++ if (ret) ++ return ret; ++ ++ return val; ++} ++ ++static const u32 valx_x10[] = { ++ 10, 11, 13, 15, 17, 20, 25, 33, 41, 50, 59, 73, 87, 100 ++}; ++static const u32 log10x_x1000[] = { ++ 0, 41, 114, 176, 230, 301, 398, 518, 613, 699, 771, 863, 939, 1000 ++}; ++ ++static u32 log10_x1000(u32 x) ++{ ++ u32 diff_val, step_val, step_log10; ++ u32 log_val = 0; ++ u32 i; ++ ++ if (x <= 0) ++ return -1000000; /* signal error */ ++ ++ if (x == 10) ++ return 0; /* log(1)=0 */ ++ ++ if (x < 10) { ++ while (x < 10) { ++ x = x * 10; ++ log_val--; ++ } ++ } else { /* x > 10 */ ++ while (x >= 100) { ++ x = x / 10; ++ log_val++; ++ } ++ } ++ log_val *= 1000; ++ ++ if (x == 10) /* was our input an exact multiple of 10 */ ++ return log_val; /* don't need to interpolate */ ++ ++ /* find our place on the log curve */ ++ for (i = 1; i < ARRAY_SIZE(valx_x10); i++) { ++ if (valx_x10[i] >= x) ++ break; ++ } ++ if (i == ARRAY_SIZE(valx_x10)) ++ return log_val + log10x_x1000[i - 1]; ++ ++ diff_val = x - valx_x10[i-1]; ++ step_val = valx_x10[i] - valx_x10[i - 1]; ++ step_log10 = log10x_x1000[i] - log10x_x1000[i - 1]; ++ ++ /* do a linear interpolation to get in-between values */ ++ return log_val + log10x_x1000[i - 1] + ++ ((diff_val*step_log10) / step_val); ++} ++ ++static u32 lgdt3306a_calculate_snr_x100(struct lgdt3306a_state *state) ++{ ++ u32 mse; /* Mean-Square Error */ ++ u32 pwr; /* Constelation power */ ++ u32 snr_x100; ++ ++ mse = (read_reg(state, 0x00ec) << 8) | ++ (read_reg(state, 0x00ed)); ++ pwr = (read_reg(state, 0x00e8) << 8) | ++ (read_reg(state, 0x00e9)); ++ ++ if (mse == 0) /* no signal */ ++ return 0; ++ ++ snr_x100 = log10_x1000((pwr * 10000) / mse) - 3000; ++ dbg_info("mse=%u, pwr=%u, snr_x100=%d\n", mse, pwr, snr_x100); ++ ++ return snr_x100; ++} ++ ++static enum lgdt3306a_lock_status ++lgdt3306a_vsb_lock_poll(struct lgdt3306a_state *state) ++{ ++ int ret; ++ u8 cnt = 0; ++ u8 packet_error; ++ u32 snr; ++ ++ for (cnt = 0; cnt < 10; cnt++) { ++ if (lgdt3306a_sync_lock_poll(state) == LG3306_UNLOCK) { ++ dbg_info("no sync lock!\n"); ++ return LG3306_UNLOCK; ++ } ++ ++ msleep(20); ++ ret = lgdt3306a_pre_monitoring(state); ++ if (ret) ++ break; ++ ++ packet_error = lgdt3306a_get_packet_error(state); ++ snr = lgdt3306a_calculate_snr_x100(state); ++ dbg_info("cnt=%d errors=%d snr=%d\n", cnt, packet_error, snr); ++ ++ if ((snr >= 1500) && (packet_error < 0xff)) ++ return LG3306_LOCK; ++ } ++ ++ dbg_info("not locked!\n"); ++ return LG3306_UNLOCK; ++} ++ ++static enum lgdt3306a_lock_status ++lgdt3306a_qam_lock_poll(struct lgdt3306a_state *state) ++{ ++ u8 cnt; ++ u8 packet_error; ++ u32 snr; ++ ++ for (cnt = 0; cnt < 10; cnt++) { ++ if (lgdt3306a_fec_lock_poll(state) == LG3306_UNLOCK) { ++ dbg_info("no fec lock!\n"); ++ return LG3306_UNLOCK; ++ } ++ ++ msleep(20); ++ ++ packet_error = lgdt3306a_get_packet_error(state); ++ snr = lgdt3306a_calculate_snr_x100(state); ++ dbg_info("cnt=%d errors=%d snr=%d\n", cnt, packet_error, snr); ++ ++ if ((snr >= 1500) && (packet_error < 0xff)) ++ return LG3306_LOCK; ++ } ++ ++ dbg_info("not locked!\n"); ++ return LG3306_UNLOCK; ++} ++ ++static int lgdt3306a_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ u16 strength = 0; ++ int ret = 0; ++ ++ if (fe->ops.tuner_ops.get_rf_strength) { ++ ret = fe->ops.tuner_ops.get_rf_strength(fe, &strength); ++ if (ret == 0) ++ dbg_info("strength=%d\n", strength); ++ else ++ dbg_info("fe->ops.tuner_ops.get_rf_strength() failed\n"); ++ } ++ ++ *status = 0; ++ if (lgdt3306a_neverlock_poll(state) == LG3306_NL_LOCK) { ++ *status |= FE_HAS_SIGNAL; ++ *status |= FE_HAS_CARRIER; ++ ++ switch (state->current_modulation) { ++ case QAM_256: ++ case QAM_64: ++ if (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) { ++ *status |= FE_HAS_VITERBI; ++ *status |= FE_HAS_SYNC; ++ ++ *status |= FE_HAS_LOCK; ++ } ++ break; ++ case VSB_8: ++ if (lgdt3306a_vsb_lock_poll(state) == LG3306_LOCK) { ++ *status |= FE_HAS_VITERBI; ++ *status |= FE_HAS_SYNC; ++ ++ *status |= FE_HAS_LOCK; ++ ++ ret = lgdt3306a_monitor_vsb(state); ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ } ++ return ret; ++} ++ ++ ++static int lgdt3306a_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ ++ state->snr = lgdt3306a_calculate_snr_x100(state); ++ /* report SNR in dB * 10 */ ++ *snr = state->snr/10; ++ ++ return 0; ++} ++ ++static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe, ++ u16 *strength) ++{ ++ /* ++ * Calculate some sort of "strength" from SNR ++ */ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ u16 snr; /* snr_x10 */ ++ int ret; ++ u32 ref_snr; /* snr*100 */ ++ u32 str; ++ ++ *strength = 0; ++ ++ switch (state->current_modulation) { ++ case VSB_8: ++ ref_snr = 1600; /* 16dB */ ++ break; ++ case QAM_64: ++ ref_snr = 2200; /* 22dB */ ++ break; ++ case QAM_256: ++ ref_snr = 2800; /* 28dB */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = fe->ops.read_snr(fe, &snr); ++ if (lg_chkerr(ret)) ++ goto fail; ++ ++ if (state->snr <= (ref_snr - 100)) ++ str = 0; ++ else if (state->snr <= ref_snr) ++ str = (0xffff * 65) / 100; /* 65% */ ++ else { ++ str = state->snr - ref_snr; ++ str /= 50; ++ str += 78; /* 78%-100% */ ++ if (str > 100) ++ str = 100; ++ str = (0xffff * str) / 100; ++ } ++ *strength = (u16)str; ++ dbg_info("strength=%u\n", *strength); ++ ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3306a_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ u32 tmp; ++ ++ *ber = 0; ++#if 1 ++ /* FGR - FIXME - I don't know what value is expected by dvb_core ++ * what is the scale of the value?? */ ++ tmp = read_reg(state, 0x00fc); /* NBERVALUE[24-31] */ ++ tmp = (tmp << 8) | read_reg(state, 0x00fd); /* NBERVALUE[16-23] */ ++ tmp = (tmp << 8) | read_reg(state, 0x00fe); /* NBERVALUE[8-15] */ ++ tmp = (tmp << 8) | read_reg(state, 0x00ff); /* NBERVALUE[0-7] */ ++ *ber = tmp; ++ dbg_info("ber=%u\n", tmp); ++#endif ++ return 0; ++} ++ ++static int lgdt3306a_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ ++ *ucblocks = 0; ++#if 1 ++ /* FGR - FIXME - I don't know what value is expected by dvb_core ++ * what happens when value wraps? */ ++ *ucblocks = read_reg(state, 0x00f4); /* TPIFTPERRCNT[0-7] */ ++ dbg_info("ucblocks=%u\n", *ucblocks); ++#endif ++ ++ return 0; ++} ++ ++static int lgdt3306a_tune(struct dvb_frontend *fe, bool re_tune, ++ unsigned int mode_flags, unsigned int *delay, ++ fe_status_t *status) ++{ ++ int ret = 0; ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ ++ dbg_info("re_tune=%u\n", re_tune); ++ ++ if (re_tune) { ++ state->current_frequency = -1; /* force re-tune */ ++ ret = lgdt3306a_set_parameters(fe); ++ if (ret != 0) ++ return ret; ++ } ++ *delay = 125; ++ ret = lgdt3306a_read_status(fe, status); ++ ++ return ret; ++} ++ ++static int lgdt3306a_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings ++ *fe_tune_settings) ++{ ++ fe_tune_settings->min_delay_ms = 100; ++ dbg_info("\n"); ++ return 0; ++} ++ ++static int lgdt3306a_search(struct dvb_frontend *fe) ++{ ++ fe_status_t status = 0; ++ int i, ret; ++ ++ /* set frontend */ ++ ret = lgdt3306a_set_parameters(fe); ++ if (ret) ++ goto error; ++ ++ /* wait frontend lock */ ++ for (i = 20; i > 0; i--) { ++ dbg_info(": loop=%d\n", i); ++ msleep(50); ++ ret = lgdt3306a_read_status(fe, &status); ++ if (ret) ++ goto error; ++ ++ if (status & FE_HAS_LOCK) ++ break; ++ } ++ ++ /* check if we have a valid signal */ ++ if (status & FE_HAS_LOCK) ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++ else ++ return DVBFE_ALGO_SEARCH_AGAIN; ++ ++error: ++ dbg_info("failed (%d)\n", ret); ++ return DVBFE_ALGO_SEARCH_ERROR; ++} ++ ++static void lgdt3306a_release(struct dvb_frontend *fe) ++{ ++ struct lgdt3306a_state *state = fe->demodulator_priv; ++ ++ dbg_info("\n"); ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops lgdt3306a_ops; ++ ++struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config, ++ struct i2c_adapter *i2c_adap) ++{ ++ struct lgdt3306a_state *state = NULL; ++ int ret; ++ u8 val; ++ ++ dbg_info("(%d-%04x)\n", ++ i2c_adap ? i2c_adapter_id(i2c_adap) : 0, ++ config ? config->i2c_addr : 0); ++ ++ state = kzalloc(sizeof(struct lgdt3306a_state), GFP_KERNEL); ++ if (state == NULL) ++ goto fail; ++ ++ state->cfg = config; ++ state->i2c_adap = i2c_adap; ++ ++ memcpy(&state->frontend.ops, &lgdt3306a_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ /* verify that we're talking to a lg3306a */ ++ /* FGR - NOTE - there is no obvious ChipId to check; we check ++ * some "known" bits after reset, but it's still just a guess */ ++ ret = lgdt3306a_read_reg(state, 0x0000, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ if ((val & 0x74) != 0x74) { ++ pr_warn("expected 0x74, got 0x%x\n", (val & 0x74)); ++#if 0 ++ /* FIXME - re-enable when we know this is right */ ++ goto fail; ++#endif ++ } ++ ret = lgdt3306a_read_reg(state, 0x0001, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ if ((val & 0xf6) != 0xc6) { ++ pr_warn("expected 0xc6, got 0x%x\n", (val & 0xf6)); ++#if 0 ++ /* FIXME - re-enable when we know this is right */ ++ goto fail; ++#endif ++ } ++ ret = lgdt3306a_read_reg(state, 0x0002, &val); ++ if (lg_chkerr(ret)) ++ goto fail; ++ if ((val & 0x73) != 0x03) { ++ pr_warn("expected 0x03, got 0x%x\n", (val & 0x73)); ++#if 0 ++ /* FIXME - re-enable when we know this is right */ ++ goto fail; ++#endif ++ } ++ ++ state->current_frequency = -1; ++ state->current_modulation = -1; ++ ++ lgdt3306a_sleep(state); ++ ++ return &state->frontend; ++ ++fail: ++ pr_warn("unable to detect LGDT3306A hardware\n"); ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(lgdt3306a_attach); ++ ++#ifdef DBG_DUMP ++ ++static const short regtab[] = { ++ 0x0000, /* SOFTRSTB 1'b1 1'b1 1'b1 ADCPDB 1'b1 PLLPDB GBBPDB 11111111 */ ++ 0x0001, /* 1'b1 1'b1 1'b0 1'b0 AUTORPTRS */ ++ 0x0002, /* NI2CRPTEN 1'b0 1'b0 1'b0 SPECINVAUT */ ++ 0x0003, /* AGCRFOUT */ ++ 0x0004, /* ADCSEL1V ADCCNT ADCCNF ADCCNS ADCCLKPLL */ ++ 0x0005, /* PLLINDIVSE */ ++ 0x0006, /* PLLCTRL[7:0] 11100001 */ ++ 0x0007, /* SYSINITWAITTIME[7:0] (msec) 00001000 */ ++ 0x0008, /* STDOPMODE[7:0] 10000000 */ ++ 0x0009, /* 1'b0 1'b0 1'b0 STDOPDETTMODE[2:0] STDOPDETCMODE[1:0] 00011110 */ ++ 0x000a, /* DAFTEN 1'b1 x x SCSYSLOCK */ ++ 0x000b, /* SCSYSLOCKCHKTIME[7:0] (10msec) 01100100 */ ++ 0x000d, /* x SAMPLING4 */ ++ 0x000e, /* SAMFREQ[15:8] 00000000 */ ++ 0x000f, /* SAMFREQ[7:0] 00000000 */ ++ 0x0010, /* IFFREQ[15:8] 01100000 */ ++ 0x0011, /* IFFREQ[7:0] 00000000 */ ++ 0x0012, /* AGCEN AGCREFMO */ ++ 0x0013, /* AGCRFFIXB AGCIFFIXB AGCLOCKDETRNGSEL[1:0] 1'b1 1'b0 1'b0 1'b0 11101000 */ ++ 0x0014, /* AGCFIXVALUE[7:0] 01111111 */ ++ 0x0015, /* AGCREF[15:8] 00001010 */ ++ 0x0016, /* AGCREF[7:0] 11100100 */ ++ 0x0017, /* AGCDELAY[7:0] 00100000 */ ++ 0x0018, /* AGCRFBW[3:0] AGCIFBW[3:0] 10001000 */ ++ 0x0019, /* AGCUDOUTMODE[1:0] AGCUDCTRLLEN[1:0] AGCUDCTRL */ ++ 0x001c, /* 1'b1 PFEN MFEN AICCVSYNC */ ++ 0x001d, /* 1'b0 1'b1 1'b0 1'b1 AICCVSYNC */ ++ 0x001e, /* AICCALPHA[3:0] 1'b1 1'b0 1'b1 1'b0 01111010 */ ++ 0x001f, /* AICCDETTH[19:16] AICCOFFTH[19:16] 00000000 */ ++ 0x0020, /* AICCDETTH[15:8] 01111100 */ ++ 0x0021, /* AICCDETTH[7:0] 00000000 */ ++ 0x0022, /* AICCOFFTH[15:8] 00000101 */ ++ 0x0023, /* AICCOFFTH[7:0] 11100000 */ ++ 0x0024, /* AICCOPMODE3[1:0] AICCOPMODE2[1:0] AICCOPMODE1[1:0] AICCOPMODE0[1:0] 00000000 */ ++ 0x0025, /* AICCFIXFREQ3[23:16] 00000000 */ ++ 0x0026, /* AICCFIXFREQ3[15:8] 00000000 */ ++ 0x0027, /* AICCFIXFREQ3[7:0] 00000000 */ ++ 0x0028, /* AICCFIXFREQ2[23:16] 00000000 */ ++ 0x0029, /* AICCFIXFREQ2[15:8] 00000000 */ ++ 0x002a, /* AICCFIXFREQ2[7:0] 00000000 */ ++ 0x002b, /* AICCFIXFREQ1[23:16] 00000000 */ ++ 0x002c, /* AICCFIXFREQ1[15:8] 00000000 */ ++ 0x002d, /* AICCFIXFREQ1[7:0] 00000000 */ ++ 0x002e, /* AICCFIXFREQ0[23:16] 00000000 */ ++ 0x002f, /* AICCFIXFREQ0[15:8] 00000000 */ ++ 0x0030, /* AICCFIXFREQ0[7:0] 00000000 */ ++ 0x0031, /* 1'b0 1'b1 1'b0 1'b0 x DAGC1STER */ ++ 0x0032, /* DAGC1STEN DAGC1STER */ ++ 0x0033, /* DAGC1STREF[15:8] 00001010 */ ++ 0x0034, /* DAGC1STREF[7:0] 11100100 */ ++ 0x0035, /* DAGC2NDE */ ++ 0x0036, /* DAGC2NDREF[15:8] 00001010 */ ++ 0x0037, /* DAGC2NDREF[7:0] 10000000 */ ++ 0x0038, /* DAGC2NDLOCKDETRNGSEL[1:0] */ ++ 0x003d, /* 1'b1 SAMGEARS */ ++ 0x0040, /* SAMLFGMA */ ++ 0x0041, /* SAMLFBWM */ ++ 0x0044, /* 1'b1 CRGEARSHE */ ++ 0x0045, /* CRLFGMAN */ ++ 0x0046, /* CFLFBWMA */ ++ 0x0047, /* CRLFGMAN */ ++ 0x0048, /* x x x x CRLFGSTEP_VS[3:0] xxxx1001 */ ++ 0x0049, /* CRLFBWMA */ ++ 0x004a, /* CRLFBWMA */ ++ 0x0050, /* 1'b0 1'b1 1'b1 1'b0 MSECALCDA */ ++ 0x0070, /* TPOUTEN TPIFEN TPCLKOUTE */ ++ 0x0071, /* TPSENB TPSSOPBITE */ ++ 0x0073, /* TP47HINS x x CHBERINT PERMODE[1:0] PERINT[1:0] 1xx11100 */ ++ 0x0075, /* x x x x x IQSWAPCTRL[2:0] xxxxx000 */ ++ 0x0076, /* NBERCON NBERST NBERPOL NBERWSYN */ ++ 0x0077, /* x NBERLOSTTH[2:0] NBERACQTH[3:0] x0000000 */ ++ 0x0078, /* NBERPOLY[31:24] 00000000 */ ++ 0x0079, /* NBERPOLY[23:16] 00000000 */ ++ 0x007a, /* NBERPOLY[15:8] 00000000 */ ++ 0x007b, /* NBERPOLY[7:0] 00000000 */ ++ 0x007c, /* NBERPED[31:24] 00000000 */ ++ 0x007d, /* NBERPED[23:16] 00000000 */ ++ 0x007e, /* NBERPED[15:8] 00000000 */ ++ 0x007f, /* NBERPED[7:0] 00000000 */ ++ 0x0080, /* x AGCLOCK DAGCLOCK SYSLOCK x x NEVERLOCK[1:0] */ ++ 0x0085, /* SPECINVST */ ++ 0x0088, /* SYSLOCKTIME[15:8] */ ++ 0x0089, /* SYSLOCKTIME[7:0] */ ++ 0x008c, /* FECLOCKTIME[15:8] */ ++ 0x008d, /* FECLOCKTIME[7:0] */ ++ 0x008e, /* AGCACCOUT[15:8] */ ++ 0x008f, /* AGCACCOUT[7:0] */ ++ 0x0090, /* AICCREJSTATUS[3:0] AICCREJBUSY[3:0] */ ++ 0x0091, /* AICCVSYNC */ ++ 0x009c, /* CARRFREQOFFSET[15:8] */ ++ 0x009d, /* CARRFREQOFFSET[7:0] */ ++ 0x00a1, /* SAMFREQOFFSET[23:16] */ ++ 0x00a2, /* SAMFREQOFFSET[15:8] */ ++ 0x00a3, /* SAMFREQOFFSET[7:0] */ ++ 0x00a6, /* SYNCLOCK SYNCLOCKH */ ++#if 0 /* covered elsewhere */ ++ 0x00e8, /* CONSTPWR[15:8] */ ++ 0x00e9, /* CONSTPWR[7:0] */ ++ 0x00ea, /* BMSE[15:8] */ ++ 0x00eb, /* BMSE[7:0] */ ++ 0x00ec, /* MSE[15:8] */ ++ 0x00ed, /* MSE[7:0] */ ++ 0x00ee, /* CONSTI[7:0] */ ++ 0x00ef, /* CONSTQ[7:0] */ ++#endif ++ 0x00f4, /* TPIFTPERRCNT[7:0] */ ++ 0x00f5, /* TPCORREC */ ++ 0x00f6, /* VBBER[15:8] */ ++ 0x00f7, /* VBBER[7:0] */ ++ 0x00f8, /* VABER[15:8] */ ++ 0x00f9, /* VABER[7:0] */ ++ 0x00fa, /* TPERRCNT[7:0] */ ++ 0x00fb, /* NBERLOCK x x x x x x x */ ++ 0x00fc, /* NBERVALUE[31:24] */ ++ 0x00fd, /* NBERVALUE[23:16] */ ++ 0x00fe, /* NBERVALUE[15:8] */ ++ 0x00ff, /* NBERVALUE[7:0] */ ++ 0x1000, /* 1'b0 WODAGCOU */ ++ 0x1005, /* x x 1'b1 1'b1 x SRD_Q_QM */ ++ 0x1009, /* SRDWAITTIME[7:0] (10msec) 00100011 */ ++ 0x100a, /* SRDWAITTIME_CQS[7:0] (msec) 01100100 */ ++ 0x101a, /* x 1'b1 1'b0 1'b0 x QMDQAMMODE[2:0] x100x010 */ ++ 0x1036, /* 1'b0 1'b1 1'b0 1'b0 SAMGSEND_CQS[3:0] 01001110 */ ++ 0x103c, /* SAMGSAUTOSTL_V[3:0] SAMGSAUTOEDL_V[3:0] 01000110 */ ++ 0x103d, /* 1'b1 1'b1 SAMCNORMBP_V[1:0] 1'b0 1'b0 SAMMODESEL_V[1:0] 11100001 */ ++ 0x103f, /* SAMZTEDSE */ ++ 0x105d, /* EQSTATUSE */ ++ 0x105f, /* x PMAPG2_V[2:0] x DMAPG2_V[2:0] x001x011 */ ++ 0x1060, /* 1'b1 EQSTATUSE */ ++ 0x1061, /* CRMAPBWSTL_V[3:0] CRMAPBWEDL_V[3:0] 00000100 */ ++ 0x1065, /* 1'b0 x CRMODE_V[1:0] 1'b1 x 1'b1 x 0x111x1x */ ++ 0x1066, /* 1'b0 1'b0 1'b1 1'b0 1'b1 PNBOOSTSE */ ++ 0x1068, /* CREPHNGAIN2_V[3:0] CREPHNPBW_V[3:0] 10010001 */ ++ 0x106e, /* x x x x x CREPHNEN_ */ ++ 0x106f, /* CREPHNTH_V[7:0] 00010101 */ ++ 0x1072, /* CRSWEEPN */ ++ 0x1073, /* CRPGAIN_V[3:0] x x 1'b1 1'b1 1001xx11 */ ++ 0x1074, /* CRPBW_V[3:0] x x 1'b1 1'b1 0001xx11 */ ++ 0x1080, /* DAFTSTATUS[1:0] x x x x x x */ ++ 0x1081, /* SRDSTATUS[1:0] x x x x x SRDLOCK */ ++ 0x10a9, /* EQSTATUS_CQS[1:0] x x x x x x */ ++ 0x10b7, /* EQSTATUS_V[1:0] x x x x x x */ ++#if 0 /* SMART_ANT */ ++ 0x1f00, /* MODEDETE */ ++ 0x1f01, /* x x x x x x x SFNRST xxxxxxx0 */ ++ 0x1f03, /* NUMOFANT[7:0] 10000000 */ ++ 0x1f04, /* x SELMASK[6:0] x0000000 */ ++ 0x1f05, /* x SETMASK[6:0] x0000000 */ ++ 0x1f06, /* x TXDATA[6:0] x0000000 */ ++ 0x1f07, /* x CHNUMBER[6:0] x0000000 */ ++ 0x1f09, /* AGCTIME[23:16] 10011000 */ ++ 0x1f0a, /* AGCTIME[15:8] 10010110 */ ++ 0x1f0b, /* AGCTIME[7:0] 10000000 */ ++ 0x1f0c, /* ANTTIME[31:24] 00000000 */ ++ 0x1f0d, /* ANTTIME[23:16] 00000011 */ ++ 0x1f0e, /* ANTTIME[15:8] 10010000 */ ++ 0x1f0f, /* ANTTIME[7:0] 10010000 */ ++ 0x1f11, /* SYNCTIME[23:16] 10011000 */ ++ 0x1f12, /* SYNCTIME[15:8] 10010110 */ ++ 0x1f13, /* SYNCTIME[7:0] 10000000 */ ++ 0x1f14, /* SNRTIME[31:24] 00000001 */ ++ 0x1f15, /* SNRTIME[23:16] 01111101 */ ++ 0x1f16, /* SNRTIME[15:8] 01111000 */ ++ 0x1f17, /* SNRTIME[7:0] 01000000 */ ++ 0x1f19, /* FECTIME[23:16] 00000000 */ ++ 0x1f1a, /* FECTIME[15:8] 01110010 */ ++ 0x1f1b, /* FECTIME[7:0] 01110000 */ ++ 0x1f1d, /* FECTHD[7:0] 00000011 */ ++ 0x1f1f, /* SNRTHD[23:16] 00001000 */ ++ 0x1f20, /* SNRTHD[15:8] 01111111 */ ++ 0x1f21, /* SNRTHD[7:0] 10000101 */ ++ 0x1f80, /* IRQFLG x x SFSDRFLG MODEBFLG SAVEFLG SCANFLG TRACKFLG */ ++ 0x1f81, /* x SYNCCON SNRCON FECCON x STDBUSY SYNCRST AGCFZCO */ ++ 0x1f82, /* x x x SCANOPCD[4:0] */ ++ 0x1f83, /* x x x x MAINOPCD[3:0] */ ++ 0x1f84, /* x x RXDATA[13:8] */ ++ 0x1f85, /* RXDATA[7:0] */ ++ 0x1f86, /* x x SDTDATA[13:8] */ ++ 0x1f87, /* SDTDATA[7:0] */ ++ 0x1f89, /* ANTSNR[23:16] */ ++ 0x1f8a, /* ANTSNR[15:8] */ ++ 0x1f8b, /* ANTSNR[7:0] */ ++ 0x1f8c, /* x x x x ANTFEC[13:8] */ ++ 0x1f8d, /* ANTFEC[7:0] */ ++ 0x1f8e, /* MAXCNT[7:0] */ ++ 0x1f8f, /* SCANCNT[7:0] */ ++ 0x1f91, /* MAXPW[23:16] */ ++ 0x1f92, /* MAXPW[15:8] */ ++ 0x1f93, /* MAXPW[7:0] */ ++ 0x1f95, /* CURPWMSE[23:16] */ ++ 0x1f96, /* CURPWMSE[15:8] */ ++ 0x1f97, /* CURPWMSE[7:0] */ ++#endif /* SMART_ANT */ ++ 0x211f, /* 1'b1 1'b1 1'b1 CIRQEN x x 1'b0 1'b0 1111xx00 */ ++ 0x212a, /* EQAUTOST */ ++ 0x2122, /* CHFAST[7:0] 01100000 */ ++ 0x212b, /* FFFSTEP_V[3:0] x FBFSTEP_V[2:0] 0001x001 */ ++ 0x212c, /* PHDEROTBWSEL[3:0] 1'b1 1'b1 1'b1 1'b0 10001110 */ ++ 0x212d, /* 1'b1 1'b1 1'b1 1'b1 x x TPIFLOCKS */ ++ 0x2135, /* DYNTRACKFDEQ[3:0] x 1'b0 1'b0 1'b0 1010x000 */ ++ 0x2141, /* TRMODE[1:0] 1'b1 1'b1 1'b0 1'b1 1'b1 1'b1 01110111 */ ++ 0x2162, /* AICCCTRLE */ ++ 0x2173, /* PHNCNFCNT[7:0] 00000100 */ ++ 0x2179, /* 1'b0 1'b0 1'b0 1'b1 x BADSINGLEDYNTRACKFBF[2:0] 0001x001 */ ++ 0x217a, /* 1'b0 1'b0 1'b0 1'b1 x BADSLOWSINGLEDYNTRACKFBF[2:0] 0001x001 */ ++ 0x217e, /* CNFCNTTPIF[7:0] 00001000 */ ++ 0x217f, /* TPERRCNTTPIF[7:0] 00000001 */ ++ 0x2180, /* x x x x x x FBDLYCIR[9:8] */ ++ 0x2181, /* FBDLYCIR[7:0] */ ++ 0x2185, /* MAXPWRMAIN[7:0] */ ++ 0x2191, /* NCOMBDET x x x x x x x */ ++ 0x2199, /* x MAINSTRON */ ++ 0x219a, /* FFFEQSTEPOUT_V[3:0] FBFSTEPOUT_V[2:0] */ ++ 0x21a1, /* x x SNRREF[5:0] */ ++ 0x2845, /* 1'b0 1'b1 x x FFFSTEP_CQS[1:0] FFFCENTERTAP[1:0] 01xx1110 */ ++ 0x2846, /* 1'b0 x 1'b0 1'b1 FBFSTEP_CQS[1:0] 1'b1 1'b0 0x011110 */ ++ 0x2847, /* ENNOSIGDE */ ++ 0x2849, /* 1'b1 1'b1 NOUSENOSI */ ++ 0x284a, /* EQINITWAITTIME[7:0] 01100100 */ ++ 0x3000, /* 1'b1 1'b1 1'b1 x x x 1'b0 RPTRSTM */ ++ 0x3001, /* RPTRSTWAITTIME[7:0] (100msec) 00110010 */ ++ 0x3031, /* FRAMELOC */ ++ 0x3032, /* 1'b1 1'b0 1'b0 1'b0 x x FRAMELOCKMODE_CQS[1:0] 1000xx11 */ ++ 0x30a9, /* VDLOCK_Q FRAMELOCK */ ++ 0x30aa, /* MPEGLOCK */ ++}; ++ ++#define numDumpRegs (sizeof(regtab)/sizeof(regtab[0])) ++static u8 regval1[numDumpRegs] = {0, }; ++static u8 regval2[numDumpRegs] = {0, }; ++ ++static void lgdt3306a_DumpAllRegs(struct lgdt3306a_state *state) ++{ ++ memset(regval2, 0xff, sizeof(regval2)); ++ lgdt3306a_DumpRegs(state); ++} ++ ++static void lgdt3306a_DumpRegs(struct lgdt3306a_state *state) ++{ ++ int i; ++ int sav_debug = debug; ++ ++ if ((debug & DBG_DUMP) == 0) ++ return; ++ debug &= ~DBG_REG; /* suppress DBG_REG during reg dump */ ++ ++ lg_debug("\n"); ++ ++ for (i = 0; i < numDumpRegs; i++) { ++ lgdt3306a_read_reg(state, regtab[i], ®val1[i]); ++ if (regval1[i] != regval2[i]) { ++ lg_debug(" %04X = %02X\n", regtab[i], regval1[i]); ++ regval2[i] = regval1[i]; ++ } ++ } ++ debug = sav_debug; ++} ++#endif /* DBG_DUMP */ ++ ++ ++ ++static struct dvb_frontend_ops lgdt3306a_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "LG Electronics LGDT3306A VSB/QAM Frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB ++ }, ++ .i2c_gate_ctrl = lgdt3306a_i2c_gate_ctrl, ++ .init = lgdt3306a_init, ++ .sleep = lgdt3306a_fe_sleep, ++ /* if this is set, it overrides the default swzigzag */ ++ .tune = lgdt3306a_tune, ++ .set_frontend = lgdt3306a_set_parameters, ++ .get_frontend = lgdt3306a_get_frontend, ++ .get_frontend_algo = lgdt3306a_get_frontend_algo, ++ .get_tune_settings = lgdt3306a_get_tune_settings, ++ .read_status = lgdt3306a_read_status, ++ .read_ber = lgdt3306a_read_ber, ++ .read_signal_strength = lgdt3306a_read_signal_strength, ++ .read_snr = lgdt3306a_read_snr, ++ .read_ucblocks = lgdt3306a_read_ucblocks, ++ .release = lgdt3306a_release, ++ .ts_bus_ctrl = lgdt3306a_ts_bus_ctrl, ++ .search = lgdt3306a_search, ++}; ++ ++MODULE_DESCRIPTION("LG Electronics LGDT3306A ATSC/QAM-B Demodulator Driver"); ++MODULE_AUTHOR("Fred Richter "); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.2"); +diff -urN a/drivers/media/dvb-frontends/lgdt3306a.h b/drivers/media/dvb-frontends/lgdt3306a.h +--- a/drivers/media/dvb-frontends/lgdt3306a.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/drivers/media/dvb-frontends/lgdt3306a.h 2015-05-05 21:22:52.244516766 +0300 +@@ -0,0 +1,74 @@ ++/* ++ * Support for LGDT3306A - 8VSB/QAM-B ++ * ++ * Copyright (C) 2013,2014 Fred Richter ++ * based on lgdt3305.[ch] by Michael Krufky ++ * ++ * 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. ++ */ ++ ++#ifndef _LGDT3306A_H_ ++#define _LGDT3306A_H_ ++ ++#include ++#include "dvb_frontend.h" ++ ++ ++enum lgdt3306a_mpeg_mode { ++ LGDT3306A_MPEG_PARALLEL = 0, ++ LGDT3306A_MPEG_SERIAL = 1, ++}; ++ ++enum lgdt3306a_tp_clock_edge { ++ LGDT3306A_TPCLK_RISING_EDGE = 0, ++ LGDT3306A_TPCLK_FALLING_EDGE = 1, ++}; ++ ++enum lgdt3306a_tp_valid_polarity { ++ LGDT3306A_TP_VALID_LOW = 0, ++ LGDT3306A_TP_VALID_HIGH = 1, ++}; ++ ++struct lgdt3306a_config { ++ u8 i2c_addr; ++ ++ /* user defined IF frequency in KHz */ ++ u16 qam_if_khz; ++ u16 vsb_if_khz; ++ ++ /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ ++ unsigned int deny_i2c_rptr:1; ++ ++ /* spectral inversion - 0:disabled 1:enabled */ ++ unsigned int spectral_inversion:1; ++ ++ enum lgdt3306a_mpeg_mode mpeg_mode; ++ enum lgdt3306a_tp_clock_edge tpclk_edge; ++ enum lgdt3306a_tp_valid_polarity tpvalid_polarity; ++ ++ /* demod clock freq in MHz; 24 or 25 supported */ ++ int xtalMHz; ++}; ++ ++#if IS_ENABLED(CONFIG_DVB_LGDT3306A) ++struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config, ++ struct i2c_adapter *i2c_adap); ++#else ++static inline ++struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config, ++ struct i2c_adapter *i2c_adap) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_LGDT3306A */ ++ ++#endif /* _LGDT3306A_H_ */ +diff -urN a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile +--- a/drivers/media/dvb-frontends/Makefile 2015-05-05 20:31:16.696408429 +0300 ++++ b/drivers/media/dvb-frontends/Makefile 2015-05-05 21:23:28.524518035 +0300 +@@ -54,6 +54,7 @@ + obj-$(CONFIG_DVB_S5H1420) += s5h1420.o + obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o + obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o ++obj-$(CONFIG_DVB_LGDT3306A) += lgdt3306a.o + obj-$(CONFIG_DVB_LG2160) += lg2160.o + obj-$(CONFIG_DVB_CX24123) += cx24123.o + obj-$(CONFIG_DVB_LNBP21) += lnbp21.o +diff -urN a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c +--- a/drivers/media/tuners/si2157.c 2015-04-29 11:22:30.000000000 +0300 ++++ b/drivers/media/tuners/si2157.c 2015-05-05 21:27:01.580525492 +0300 +@@ -244,6 +244,7 @@ + int ret; + struct si2157_cmd cmd; + u8 bandwidth, delivery_system; ++ u32 if_frequency = 5000000; + + dev_dbg(&client->dev, + "delivery_system=%d frequency=%u bandwidth_hz=%u\n", +@@ -266,9 +267,11 @@ + switch (c->delivery_system) { + case SYS_ATSC: + delivery_system = 0x00; ++ if_frequency = 3250000; + break; + case SYS_DVBC_ANNEX_B: + delivery_system = 0x10; ++ if_frequency = 4000000; + break; + case SYS_DVBT: + case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ +@@ -302,6 +305,20 @@ + if (ret) + goto err; + ++ /* set if frequency if needed */ ++ if (if_frequency != dev->if_frequency) { ++ memcpy(cmd.args, "\x14\x00\x06\x07", 4); ++ cmd.args[4] = (if_frequency / 1000) & 0xff; ++ cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff; ++ cmd.wlen = 6; ++ cmd.rlen = 4; ++ ret = si2157_cmd_execute(client, &cmd); ++ if (ret) ++ goto err; ++ ++ dev->if_frequency = if_frequency; ++ } ++ + /* set frequency */ + memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); + cmd.args[4] = (c->frequency >> 0) & 0xff; +@@ -322,14 +339,17 @@ + + static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) + { +- *frequency = 5000000; /* default value of property 0x0706 */ ++ struct i2c_client *client = fe->tuner_priv; ++ struct si2157_dev *dev = i2c_get_clientdata(client); ++ ++ *frequency = dev->if_frequency; + return 0; + } + + static const struct dvb_tuner_ops si2157_ops = { + .info = { + .name = "Silicon Labs Si2146/2147/2148/2157/2158", +- .frequency_min = 110000000, ++ .frequency_min = 55000000, + .frequency_max = 862000000, + }, + +@@ -360,6 +380,7 @@ + dev->inversion = cfg->inversion; + dev->fw_loaded = false; + dev->chiptype = (u8)id->driver_data; ++ dev->if_frequency = 5000000; /* default value of property 0x0706 */ + mutex_init(&dev->i2c_mutex); + + /* check if the tuner is there */ +diff -urN a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h +--- a/drivers/media/tuners/si2157_priv.h 2015-04-29 11:22:30.000000000 +0300 ++++ b/drivers/media/tuners/si2157_priv.h 2015-05-05 21:26:34.268524536 +0300 +@@ -28,6 +28,7 @@ + bool fw_loaded; + bool inversion; + u8 chiptype; ++ u32 if_frequency; + }; + + #define SI2157_CHIPTYPE_SI2157 0 +diff -urN a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c +--- a/drivers/media/usb/cx231xx/cx231xx-cards.c 2015-04-29 11:22:30.000000000 +0300 ++++ b/drivers/media/usb/cx231xx/cx231xx-cards.c 2015-05-05 21:27:25.452526327 +0300 +@@ -776,6 +776,45 @@ + .gpio = NULL, + } }, + }, ++ [CX231XX_BOARD_HAUPPAUGE_955Q] = { ++ .name = "Hauppauge WinTV-HVR-955Q (111401)", ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = 0x60, ++ .tuner_gpio = RDE250_XCV_TUNER, ++ .tuner_sif_gpio = 0x05, ++ .tuner_scl_gpio = 0x1a, ++ .tuner_sda_gpio = 0x1b, ++ .decoder = CX231XX_AVDECODER, ++ .output_mode = OUT_MODE_VIP11, ++ .demod_xfer_mode = 0, ++ .ctl_pin_status_mask = 0xFFFFFFC4, ++ .agc_analog_digital_select_gpio = 0x0c, ++ .gpio_pin_status_mask = 0x4001000, ++ .tuner_i2c_master = I2C_1_MUX_3, ++ .demod_i2c_master = I2C_2, ++ .has_dvb = 1, ++ .demod_addr = 0x0e, ++ .norm = V4L2_STD_NTSC, ++ ++ .input = {{ ++ .type = CX231XX_VMUX_TELEVISION, ++ .vmux = CX231XX_VIN_3_1, ++ .amux = CX231XX_AMUX_VIDEO, ++ .gpio = NULL, ++ }, { ++ .type = CX231XX_VMUX_COMPOSITE1, ++ .vmux = CX231XX_VIN_2_1, ++ .amux = CX231XX_AMUX_LINE_IN, ++ .gpio = NULL, ++ }, { ++ .type = CX231XX_VMUX_SVIDEO, ++ .vmux = CX231XX_VIN_1_1 | ++ (CX231XX_VIN_1_2 << 8) | ++ CX25840_SVIDEO_ON, ++ .amux = CX231XX_AMUX_LINE_IN, ++ .gpio = NULL, ++ } }, ++ }, + }; + const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); + +@@ -805,6 +844,8 @@ + .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC}, + {USB_DEVICE(0x2040, 0xb120), + .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, ++ {USB_DEVICE(0x2040, 0xb123), ++ .driver_info = CX231XX_BOARD_HAUPPAUGE_955Q}, + {USB_DEVICE(0x2040, 0xb130), + .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx}, + {USB_DEVICE(0x2040, 0xb131), +@@ -1052,6 +1093,7 @@ + switch (dev->model) { + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: + case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: ++ case CX231XX_BOARD_HAUPPAUGE_955Q: + { + struct tveeprom tvee; + static u8 eeprom[256]; +diff -urN a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c +--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c 2015-04-29 11:22:30.000000000 +0300 ++++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c 2015-05-05 21:27:25.452526327 +0300 +@@ -34,6 +34,7 @@ + #include "si2165.h" + #include "mb86a20s.h" + #include "si2157.h" ++#include "lgdt3306a.h" + + MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); + MODULE_AUTHOR("Srinivasa Deevi "); +@@ -160,6 +161,18 @@ + .ref_freq_Hz = 24000000, + }; + ++static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { ++ .i2c_addr = 0x59, ++ .qam_if_khz = 4000, ++ .vsb_if_khz = 3250, ++ .deny_i2c_rptr = 1, ++ .spectral_inversion = 1, ++ .mpeg_mode = LGDT3306A_MPEG_SERIAL, ++ .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, ++ .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, ++ .xtalMHz = 25, ++}; ++ + static inline void print_err_status(struct cx231xx *dev, int packet, int status) + { + char *errmsg = "Unknown"; +@@ -807,7 +820,61 @@ + dev->dvb->i2c_client_tuner = client; + break; + } ++ case CX231XX_BOARD_HAUPPAUGE_955Q: ++ { ++ struct i2c_client *client; ++ struct i2c_board_info info; ++ struct si2157_config si2157_config; ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ ++ dev->dvb->frontend = dvb_attach(lgdt3306a_attach, ++ &hauppauge_955q_lgdt3306a_config, ++ tuner_i2c ++ ); ++ ++ if (dev->dvb->frontend == NULL) { ++ dev_err(dev->dev, ++ "Failed to attach LGDT3306A frontend.\n"); ++ result = -EINVAL; ++ goto out_free; ++ } ++ ++ dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; ++ ++ /* define general-purpose callback pointer */ ++ dvb->frontend->callback = cx231xx_tuner_callback; ++ ++ /* attach tuner */ ++ memset(&si2157_config, 0, sizeof(si2157_config)); ++ si2157_config.fe = dev->dvb->frontend; ++ si2157_config.inversion = true; ++ strlcpy(info.type, "si2157", I2C_NAME_SIZE); ++ info.addr = 0x60; ++ info.platform_data = &si2157_config; ++ request_module("si2157"); ++ ++ client = i2c_new_device( ++ tuner_i2c, ++ &info); ++ if (client == NULL || client->dev.driver == NULL) { ++ dvb_frontend_detach(dev->dvb->frontend); ++ result = -ENODEV; ++ goto out_free; ++ } ++ ++ if (!try_module_get(client->dev.driver->owner)) { ++ i2c_unregister_device(client); ++ dvb_frontend_detach(dev->dvb->frontend); ++ result = -ENODEV; ++ goto out_free; ++ } ++ ++ dev->cx231xx_reset_analog_tuner = NULL; + ++ dev->dvb->i2c_client_tuner = client; ++ break; ++ } + case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: + case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID: + +diff -urN a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h +--- a/drivers/media/usb/cx231xx/cx231xx.h 2015-04-29 11:22:30.000000000 +0300 ++++ b/drivers/media/usb/cx231xx/cx231xx.h 2015-05-05 21:27:25.452526327 +0300 +@@ -76,6 +76,7 @@ + #define CX231XX_BOARD_KWORLD_UB445_USB_HYBRID 18 + #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx 19 + #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx 20 ++#define CX231XX_BOARD_HAUPPAUGE_955Q 21 + + /* Limits minimum and default number of buffers */ + #define CX231XX_MIN_BUF 4 +diff -urN a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig +--- a/drivers/media/usb/cx231xx/Kconfig 2015-04-29 11:22:30.000000000 +0300 ++++ b/drivers/media/usb/cx231xx/Kconfig 2015-05-05 21:27:25.452526327 +0300 +@@ -47,6 +47,7 @@ + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LGDT3306A if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT +diff -urN a/index.html b/index.html +--- a/index.html 1970-01-01 02:00:00.000000000 +0200 ++++ b/index.html 2015-05-05 21:27:21.300526182 +0300 +@@ -0,0 +1,184 @@ ++diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig ++index 173c0e2..0cced3e 100644 ++--- a/drivers/media/usb/cx231xx/Kconfig +++++ b/drivers/media/usb/cx231xx/Kconfig ++@@ -47,6 +47,7 @@ config VIDEO_CX231XX_DVB ++ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT +++ select DVB_LGDT3306A if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT ++diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c ++index dfc7010c..39004a1 100644 ++--- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++++ b/drivers/media/usb/cx231xx/cx231xx-cards.c ++@@ -776,6 +776,45 @@ struct cx231xx_board cx231xx_boards[] = { ++ .gpio = NULL, ++ } }, ++ }, +++ [CX231XX_BOARD_HAUPPAUGE_955Q] = { +++ .name = "Hauppauge WinTV-HVR-955Q (111401)", +++ .tuner_type = TUNER_ABSENT, +++ .tuner_addr = 0x60, +++ .tuner_gpio = RDE250_XCV_TUNER, +++ .tuner_sif_gpio = 0x05, +++ .tuner_scl_gpio = 0x1a, +++ .tuner_sda_gpio = 0x1b, +++ .decoder = CX231XX_AVDECODER, +++ .output_mode = OUT_MODE_VIP11, +++ .demod_xfer_mode = 0, +++ .ctl_pin_status_mask = 0xFFFFFFC4, +++ .agc_analog_digital_select_gpio = 0x0c, +++ .gpio_pin_status_mask = 0x4001000, +++ .tuner_i2c_master = I2C_1_MUX_3, +++ .demod_i2c_master = I2C_2, +++ .has_dvb = 1, +++ .demod_addr = 0x0e, +++ .norm = V4L2_STD_NTSC, +++ +++ .input = {{ +++ .type = CX231XX_VMUX_TELEVISION, +++ .vmux = CX231XX_VIN_3_1, +++ .amux = CX231XX_AMUX_VIDEO, +++ .gpio = NULL, +++ }, { +++ .type = CX231XX_VMUX_COMPOSITE1, +++ .vmux = CX231XX_VIN_2_1, +++ .amux = CX231XX_AMUX_LINE_IN, +++ .gpio = NULL, +++ }, { +++ .type = CX231XX_VMUX_SVIDEO, +++ .vmux = CX231XX_VIN_1_1 | +++ (CX231XX_VIN_1_2 << 8) | +++ CX25840_SVIDEO_ON, +++ .amux = CX231XX_AMUX_LINE_IN, +++ .gpio = NULL, +++ } }, +++ }, ++ }; ++ const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); ++ ++@@ -805,6 +844,8 @@ struct usb_device_id cx231xx_id_table[] = { ++ .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC}, ++ {USB_DEVICE(0x2040, 0xb120), ++ .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER}, +++ {USB_DEVICE(0x2040, 0xb123), +++ .driver_info = CX231XX_BOARD_HAUPPAUGE_955Q}, ++ {USB_DEVICE(0x2040, 0xb130), ++ .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx}, ++ {USB_DEVICE(0x2040, 0xb131), ++@@ -1049,6 +1090,7 @@ void cx231xx_card_setup(struct cx231xx *dev) ++ switch (dev->model) { ++ case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: ++ case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: +++ case CX231XX_BOARD_HAUPPAUGE_955Q: ++ { ++ struct tveeprom tvee; ++ static u8 eeprom[256]; ++diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c ++index e8c054c..140c997 100644 ++--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c ++@@ -34,6 +34,7 @@ ++ #include "si2165.h" ++ #include "mb86a20s.h" ++ #include "si2157.h" +++#include "lgdt3306a.h" ++ ++ MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); ++ MODULE_AUTHOR("Srinivasa Deevi "); ++@@ -160,6 +161,18 @@ static const struct si2165_config pctv_quatro_stick_1114xx_si2165_config = { ++ .ref_freq_Hz = 24000000, ++ }; ++ +++static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { +++ .i2c_addr = 0x59, +++ .qam_if_khz = 4000, +++ .vsb_if_khz = 3250, +++ .deny_i2c_rptr = 1, +++ .spectral_inversion = 1, +++ .mpeg_mode = LGDT3306A_MPEG_SERIAL, +++ .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, +++ .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, +++ .xtalMHz = 25, +++}; +++ ++ static inline void print_err_status(struct cx231xx *dev, int packet, int status) ++ { ++ char *errmsg = "Unknown"; ++@@ -812,7 +825,61 @@ static int dvb_init(struct cx231xx *dev) ++ dev->dvb->i2c_client_tuner = client; ++ break; ++ } +++ case CX231XX_BOARD_HAUPPAUGE_955Q: +++ { +++ struct i2c_client *client; +++ struct i2c_board_info info; +++ struct si2157_config si2157_config; +++ +++ memset(&info, 0, sizeof(struct i2c_board_info)); +++ +++ dev->dvb->frontend = dvb_attach(lgdt3306a_attach, +++ &hauppauge_955q_lgdt3306a_config, +++ tuner_i2c +++ ); +++ +++ if (dev->dvb->frontend == NULL) { +++ dev_err(dev->dev, +++ "Failed to attach LGDT3306A frontend.\n"); +++ result = -EINVAL; +++ goto out_free; +++ } +++ +++ dev->dvb->frontend->ops.i2c_gate_ctrl = NULL; +++ +++ /* define general-purpose callback pointer */ +++ dvb->frontend->callback = cx231xx_tuner_callback; +++ +++ /* attach tuner */ +++ memset(&si2157_config, 0, sizeof(si2157_config)); +++ si2157_config.fe = dev->dvb->frontend; +++ si2157_config.inversion = true; +++ strlcpy(info.type, "si2157", I2C_NAME_SIZE); +++ info.addr = 0x60; +++ info.platform_data = &si2157_config; +++ request_module("si2157"); +++ +++ client = i2c_new_device( +++ tuner_i2c, +++ &info); +++ if (client == NULL || client->dev.driver == NULL) { +++ dvb_frontend_detach(dev->dvb->frontend); +++ result = -ENODEV; +++ goto out_free; +++ } +++ +++ if (!try_module_get(client->dev.driver->owner)) { +++ i2c_unregister_device(client); +++ dvb_frontend_detach(dev->dvb->frontend); +++ result = -ENODEV; +++ goto out_free; +++ } +++ +++ dev->cx231xx_reset_analog_tuner = NULL; ++ +++ dev->dvb->i2c_client_tuner = client; +++ break; +++ } ++ case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: ++ case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID: ++ ++diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h ++index e0d3106..573b3c0 100644 ++--- a/drivers/media/usb/cx231xx/cx231xx.h +++++ b/drivers/media/usb/cx231xx/cx231xx.h ++@@ -76,6 +76,7 @@ ++ #define CX231XX_BOARD_KWORLD_UB445_USB_HYBRID 18 ++ #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx 19 ++ #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx 20 +++#define CX231XX_BOARD_HAUPPAUGE_955Q 21 ++ ++ /* Limits minimum and default number of buffers */ ++ #define CX231XX_MIN_BUF 4 diff --git a/projects/Generic/linux/linux.x86_64.conf b/projects/Generic/linux/linux.x86_64.conf index ef55ea4c27..8335e43fbf 100644 --- a/projects/Generic/linux/linux.x86_64.conf +++ b/projects/Generic/linux/linux.x86_64.conf @@ -2760,6 +2760,7 @@ CONFIG_DVB_OR51132=m CONFIG_DVB_BCM3510=m CONFIG_DVB_LGDT330X=m CONFIG_DVB_LGDT3305=m +CONFIG_DVB_LGDT3306A=m CONFIG_DVB_S5H1409=m CONFIG_DVB_AU8522=m CONFIG_DVB_AU8522_DTV=m diff --git a/projects/RPi/linux/linux.arm.conf b/projects/RPi/linux/linux.arm.conf index d850f4ec9d..f8d29c57c6 100644 --- a/projects/RPi/linux/linux.arm.conf +++ b/projects/RPi/linux/linux.arm.conf @@ -2190,6 +2190,7 @@ CONFIG_DVB_NXT200X=m CONFIG_DVB_BCM3510=m CONFIG_DVB_LGDT330X=m CONFIG_DVB_LGDT3305=m +CONFIG_DVB_LGDT3306A=m CONFIG_DVB_S5H1409=m CONFIG_DVB_AU8522=m CONFIG_DVB_AU8522_DTV=m diff --git a/projects/RPi2/linux/linux.arm.conf b/projects/RPi2/linux/linux.arm.conf index e936b60ca4..c2af403d79 100644 --- a/projects/RPi2/linux/linux.arm.conf +++ b/projects/RPi2/linux/linux.arm.conf @@ -2242,6 +2242,7 @@ CONFIG_DVB_NXT200X=m CONFIG_DVB_BCM3510=m CONFIG_DVB_LGDT330X=m CONFIG_DVB_LGDT3305=m +CONFIG_DVB_LGDT3306A=m CONFIG_DVB_S5H1409=m CONFIG_DVB_AU8522=m CONFIG_DVB_AU8522_DTV=m