diff --git a/packages/linux-driver-addons/dvb/depends/media_tree/package.mk b/packages/linux-driver-addons/dvb/depends/media_tree/package.mk
index 19eababda8..67f7884d8c 100644
--- a/packages/linux-driver-addons/dvb/depends/media_tree/package.mk
+++ b/packages/linux-driver-addons/dvb/depends/media_tree/package.mk
@@ -17,8 +17,8 @@
################################################################################
PKG_NAME="media_tree"
-PKG_VERSION="2017-11-14-f2ecc3d0787e"
-PKG_SHA256="54aaa4cb2ab34804f42410dd461e587ecae36bd808c6e4accb3bcdae8c04579b"
+PKG_VERSION="2017-12-06-b32a2b42f76c"
+PKG_SHA256="90a6b5b015bbb5583a6c72880f8b89ed8b3671ca64c713a00ec3467fbb84cdc4"
PKG_ARCH="any"
PKG_LICENSE="GPL"
PKG_SITE="https://git.linuxtv.org/media_tree.git"
diff --git a/packages/linux-driver-addons/dvb/hauppauge/changelog.txt b/packages/linux-driver-addons/dvb/hauppauge/changelog.txt
new file mode 100755
index 0000000000..32d81ca428
--- /dev/null
+++ b/packages/linux-driver-addons/dvb/hauppauge/changelog.txt
@@ -0,0 +1,2 @@
+100
+- Initial add-on
diff --git a/packages/linux-driver-addons/dvb/hauppauge/icon/icon.png b/packages/linux-driver-addons/dvb/hauppauge/icon/icon.png
new file mode 100644
index 0000000000..962157e96e
Binary files /dev/null and b/packages/linux-driver-addons/dvb/hauppauge/icon/icon.png differ
diff --git a/packages/linux-driver-addons/dvb/hauppauge/package.mk b/packages/linux-driver-addons/dvb/hauppauge/package.mk
new file mode 100644
index 0000000000..925a8bed6b
--- /dev/null
+++ b/packages/linux-driver-addons/dvb/hauppauge/package.mk
@@ -0,0 +1,51 @@
+################################################################################
+# This file is part of LibreELEC - https://libreelec.tv
+# Copyright (C) 2016-present Team LibreELEC
+#
+# LibreELEC 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.
+#
+# LibreELEC 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 LibreELEC. If not, see .
+################################################################################
+
+PKG_NAME="hauppauge"
+PKG_VERSION="f5a5e5e"
+PKG_SHA256="6a3167c9990fa96838f4746861edb4d4e656739ea08d4f993e54becb9f2e9ab2"
+PKG_ARCH="any"
+PKG_LICENSE="GPL"
+PKG_SITE="http://git.linuxtv.org/media_build.git"
+PKG_URL="https://git.linuxtv.org/media_build.git/snapshot/${PKG_VERSION}.tar.gz"
+PKG_SOURCE_DIR="${PKG_VERSION}"
+PKG_DEPENDS_TARGET="toolchain linux media_tree"
+PKG_NEED_UNPACK="$LINUX_DEPENDS media_tree"
+PKG_SECTION="driver.dvb"
+PKG_LONGDESC="DVB drivers for Hauppauge"
+
+PKG_IS_ADDON="yes"
+PKG_ADDON_IS_STANDALONE="yes"
+PKG_ADDON_NAME="DVB drivers for Hauppauge"
+PKG_ADDON_TYPE="xbmc.service"
+PKG_ADDON_VERSION="${ADDON_VERSION}.${PKG_REV}"
+
+pre_make_target() {
+ export KERNEL_VER=$(get_module_dir)
+ export LDFLAGS=""
+}
+
+make_target() {
+ cp -RP $(get_build_dir media_tree)/* $PKG_BUILD/linux
+ make VER=$KERNEL_VER SRCDIR=$(kernel_path) stagingconfig
+ make VER=$KERNEL_VER SRCDIR=$(kernel_path)
+}
+
+makeinstall_target() {
+ install_driver_addon_files "$PKG_BUILD/v4l/"
+}
diff --git a/packages/linux-driver-addons/dvb/hauppauge/patches/driver.dvb.hauppauge-01-remove-rmmod.pl.patch b/packages/linux-driver-addons/dvb/hauppauge/patches/driver.dvb.hauppauge-01-remove-rmmod.pl.patch
new file mode 100644
index 0000000000..13435136c9
--- /dev/null
+++ b/packages/linux-driver-addons/dvb/hauppauge/patches/driver.dvb.hauppauge-01-remove-rmmod.pl.patch
@@ -0,0 +1,11 @@
+diff --git a/v4l/Makefile b/v4l/Makefile
+--- a/v4l/Makefile
++++ b/v4l/Makefile
+@@ -51,7 +51,6 @@ default:: prepare firmware
+ @echo Kernel build directory is $(OUTDIR)
+ $(MAKE) -C ../linux apply_patches
+ $(MAKE) -C $(OUTDIR) SUBDIRS=$(PWD) $(MYCFLAGS) modules
+- ./scripts/rmmod.pl check
+ # $(MAKE) checkpatch
+
+ mismatch:: prepare firmware
diff --git a/packages/linux-driver-addons/dvb/hauppauge/patches/driver.dvb.hauppauge-02-add-to-backports.patch b/packages/linux-driver-addons/dvb/hauppauge/patches/driver.dvb.hauppauge-02-add-to-backports.patch
new file mode 100644
index 0000000000..e5c1152a5c
--- /dev/null
+++ b/packages/linux-driver-addons/dvb/hauppauge/patches/driver.dvb.hauppauge-02-add-to-backports.patch
@@ -0,0 +1,11 @@
+--- a/backports/backports.txt
++++ b/backports/backports.txt
+@@ -25,6 +25,8 @@ add api_version.patch
+ add pr_fmt.patch
+ add debug.patch
+ add drx39xxj.patch
++add temp_revert.patch
++add hauppauge.patch
+
+ [4.10.255]
+ add v4.10_sched_signal.patch
diff --git a/packages/linux-driver-addons/dvb/hauppauge/source/default.py b/packages/linux-driver-addons/dvb/hauppauge/source/default.py
new file mode 100644
index 0000000000..fe3ba645a6
--- /dev/null
+++ b/packages/linux-driver-addons/dvb/hauppauge/source/default.py
@@ -0,0 +1,17 @@
+################################################################################
+# This file is part of LibreELEC - https://libreelec.tv
+# Copyright (C) 2017-present Team LibreELEC
+#
+# LibreELEC 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.
+#
+# LibreELEC 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 LibreELEC. If not, see .
+################################################################################
diff --git a/packages/linux-driver-addons/dvb/hauppauge/sources/backports/hauppauge.patch b/packages/linux-driver-addons/dvb/hauppauge/sources/backports/hauppauge.patch
new file mode 100644
index 0000000000..1a3520f02f
--- /dev/null
+++ b/packages/linux-driver-addons/dvb/hauppauge/sources/backports/hauppauge.patch
@@ -0,0 +1,3214 @@
+Combined patches from https://github.com/b-rad-NDi/Ubuntu-media-tree-kernel-builder
+to support all kind of Hauppauge DVB cards.
+
+diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c
+index 724e9aa..c4fefc2 100644
+--- a/drivers/media/dvb-frontends/lgdt3306a.c
++++ b/drivers/media/dvb-frontends/lgdt3306a.c
+@@ -30,6 +30,17 @@ static int debug;
+ module_param(debug, int, 0644);
+ MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))");
+
++/*
++ * Older drivers treated QAM64 and QAM256 the same; that is the HW always
++ * used "Auto" mode during detection. Setting "forced_manual"=1 allows
++ * the user to treat these modes as separate. For backwards compatibility,
++ * it's off by default. QAM_AUTO can now be specified to achive that
++ * effect even if "forced_manual"=1
++ */
++static int forced_manual;
++module_param(forced_manual, int, 0644);
++MODULE_PARM_DESC(forced_manual, "if set, QAM64 and QAM256 will only lock to modulation specified");
++
+ #define DBG_INFO 1
+ #define DBG_REG 2
+ #define DBG_DUMP 4 /* FGR - comment out to remove dump code */
+@@ -566,7 +577,12 @@ static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation)
+ /* 3. : 64QAM/256QAM detection(manual, auto) */
+ ret = lgdt3306a_read_reg(state, 0x0009, &val);
+ val &= 0xfc;
+- val |= 0x02; /* STDOPDETCMODE[1:0]=1=Manual 2=Auto */
++ /* Check for forced Manual modulation modes; otherwise always "auto" */
++ if(forced_manual && (modulation != QAM_AUTO)){
++ val |= 0x01; /* STDOPDETCMODE[1:0]= 1=Manual */
++ } else {
++ val |= 0x02; /* STDOPDETCMODE[1:0]= 2=Auto */
++ }
+ ret = lgdt3306a_write_reg(state, 0x0009, val);
+ if (lg_chkerr(ret))
+ goto fail;
+@@ -598,6 +614,28 @@ static int lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation)
+ if (lg_chkerr(ret))
+ goto fail;
+
++ /* 5.1 V0.36 SRDCHKALWAYS : For better QAM detection */
++ ret = lgdt3306a_read_reg(state, 0x000A, &val);
++ val &= 0xFD;
++ val |= 0x02;
++ ret = lgdt3306a_write_reg(state, 0x000A, val);
++ if (lg_chkerr(ret))
++ goto fail;
++
++ /* 5.2 V0.36 Control of "no signal" detector function */
++ ret = lgdt3306a_read_reg(state, 0x2849, &val);
++ val &= 0xDF;
++ ret = lgdt3306a_write_reg(state, 0x2849, val);
++ if (lg_chkerr(ret))
++ goto fail;
++
++ /* 5.3 Fix for Blonder Tongue HDE-2H-QAM and AQM modulators */
++ ret = lgdt3306a_read_reg(state, 0x302B, &val);
++ val &= 0x7F; /* SELFSYNCFINDEN_CQS=0; disable auto reset */
++ ret = lgdt3306a_write_reg(state, 0x302B, val);
++ if (lg_chkerr(ret))
++ goto fail;
++
+ /* 6. Reset */
+ ret = lgdt3306a_soft_reset(state);
+ if (lg_chkerr(ret))
+@@ -620,10 +658,9 @@ static int lgdt3306a_set_modulation(struct lgdt3306a_state *state,
+ 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);
++ case QAM_AUTO:
++ ret = lgdt3306a_set_qam(state, p->modulation);
+ break;
+ default:
+ return -EINVAL;
+@@ -650,6 +687,7 @@ static int lgdt3306a_agc_setup(struct lgdt3306a_state *state,
+ break;
+ case QAM_64:
+ case QAM_256:
++ case QAM_AUTO:
+ break;
+ default:
+ return -EINVAL;
+@@ -704,6 +742,7 @@ static int lgdt3306a_spectral_inversion(struct lgdt3306a_state *state,
+ break;
+ case QAM_64:
+ case QAM_256:
++ case QAM_AUTO:
+ /* Auto ok for QAM */
+ ret = lgdt3306a_set_inversion_auto(state, 1);
+ break;
+@@ -727,6 +766,7 @@ static int lgdt3306a_set_if(struct lgdt3306a_state *state,
+ break;
+ case QAM_64:
+ case QAM_256:
++ case QAM_AUTO:
+ if_freq_khz = state->cfg->qam_if_khz;
+ break;
+ default:
+@@ -1585,6 +1625,7 @@ static int lgdt3306a_read_status(struct dvb_frontend *fe,
+ switch (state->current_modulation) {
+ case QAM_256:
+ case QAM_64:
++ case QAM_AUTO:
+ if (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) {
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+@@ -1628,6 +1669,7 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe,
+ * Calculate some sort of "strength" from SNR
+ */
+ struct lgdt3306a_state *state = fe->demodulator_priv;
++ u8 val;
+ u16 snr; /* snr_x10 */
+ int ret;
+ u32 ref_snr; /* snr*100 */
+@@ -1640,11 +1682,15 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe,
+ ref_snr = 1600; /* 16dB */
+ break;
+ case QAM_64:
+- ref_snr = 2200; /* 22dB */
+- break;
+ case QAM_256:
+- ref_snr = 2800; /* 28dB */
+- break;
++ case QAM_AUTO:
++ /* need to know actual modulation to set proper SNR baseline */
++ lgdt3306a_read_reg(state, 0x00a6, &val);
++ if(val & 0x04)
++ ref_snr = 2800; /* QAM-256 28dB */
++ else
++ ref_snr = 2200; /* QAM-64 22dB */
++ break;
+ default:
+ return -EINVAL;
+ }
+@@ -2114,7 +2160,7 @@ static const struct dvb_frontend_ops lgdt3306a_ops = {
+ .frequency_min = 54000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 62500,
+- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
++ .caps = FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+ .i2c_gate_ctrl = lgdt3306a_i2c_gate_ctrl,
+ .init = lgdt3306a_init,
+@@ -2177,6 +2223,7 @@ static int lgdt3306a_probe(struct i2c_client *client,
+
+ i2c_set_clientdata(client, fe->demodulator_priv);
+ state = fe->demodulator_priv;
++ state->frontend.ops.release = NULL;
+
+ /* create mux i2c adapter for tuner */
+ state->muxc = i2c_mux_alloc(client->adapter, &client->dev,
+diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
+index 41d9c51..3752bb2 100644
+--- a/drivers/media/dvb-frontends/si2168.c
++++ b/drivers/media/dvb-frontends/si2168.c
+@@ -85,7 +85,7 @@ static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status)
+ struct i2c_client *client = fe->demodulator_priv;
+ struct si2168_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+- int ret, i;
++ int ret, i, sys;
+ unsigned int utmp, utmp1, utmp2;
+ struct si2168_cmd cmd;
+
+@@ -96,7 +96,21 @@ static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status)
+ goto err;
+ }
+
+- switch (c->delivery_system) {
++ memcpy(cmd.args, "\x87\x01", 2);
++ cmd.wlen = 2;
++ cmd.rlen = 8;
++ ret = si2168_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++
++ sys = c->delivery_system;
++ /* check if we found DVBT2 during DVBT tuning */
++ if (sys == SYS_DVBT) {
++ if ((cmd.args[3] & 0x0f) == 7) {
++ sys = SYS_DVBT2;
++ }
++ }
++ switch (sys) {
+ case SYS_DVBT:
+ memcpy(cmd.args, "\xa0\x01", 2);
+ cmd.wlen = 2;
+@@ -211,6 +225,7 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
+ struct i2c_client *client = fe->demodulator_priv;
+ struct si2168_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
++ struct si2168_config *config = client->dev.platform_data;
+ int ret;
+ struct si2168_cmd cmd;
+ u8 bandwidth, delivery_system;
+@@ -228,7 +243,7 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+- delivery_system = 0x20;
++ delivery_system = 0xf0; /* T/T2 auto-detect is user friendly */
+ break;
+ case SYS_DVBC_ANNEX_A:
+ delivery_system = 0x30;
+@@ -298,6 +313,16 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
+ ret = si2168_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
++ } else if (c->delivery_system == SYS_DVBT) {
++ /* select Auto PLP */
++ cmd.args[0] = 0x52;
++ cmd.args[1] = 0;
++ cmd.args[2] = 0; /* Auto PLP */
++ cmd.wlen = 3;
++ cmd.rlen = 1;
++ ret = si2168_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
+ }
+
+ memcpy(cmd.args, "\x51\x03", 2);
+@@ -337,6 +362,10 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
+
+ memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6);
+ cmd.args[4] = delivery_system | bandwidth;
++ if (delivery_system == 0xf0)
++ cmd.args[5] |= 2; /* Auto detect DVB-T/T2 */
++ if (config->inversion) /* inverted spectrum, eg si2157 */
++ cmd.args[5] |= 1;
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2168_cmd_execute(client, &cmd);
+@@ -356,6 +385,8 @@ static int si2168_set_frontend(struct dvb_frontend *fe)
+ }
+
+ memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6);
++ /* BUGBUG? FW defaults to 1, but windows driver uses 30; above is 0? */
++ cmd.args[5] = 30;
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2168_cmd_execute(client, &cmd);
+diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h
+index 3225d0c..0f71233 100644
+--- a/drivers/media/dvb-frontends/si2168.h
++++ b/drivers/media/dvb-frontends/si2168.h
+@@ -45,6 +45,9 @@ struct si2168_config {
+
+ /* TS clock gapped */
+ bool ts_clock_gapped;
++
++ /* Spectral Inversion */
++ bool inversion;
+ };
+
+ #endif
+diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
+index 940c8b1..9d2bb6d 100644
+--- a/drivers/media/i2c/cx25840/cx25840-core.c
++++ b/drivers/media/i2c/cx25840/cx25840-core.c
+@@ -668,14 +668,14 @@ static void cx23885_initialize(struct i2c_client *client)
+ */
+ cx25840_write4(client, 0x404, 0x0010253e);
+
+- /* CC on - Undocumented Register */
++ /* CC on - VBI_LINE_CTRL3, FLD_VBI_MD_LINE12 */
+ cx25840_write(client, state->vbi_regs_offset + 0x42f, 0x66);
+
+ /* HVR-1250 / HVR1850 DIF related */
+ /* Power everything up */
+ cx25840_write4(client, 0x130, 0x0);
+
+- /* Undocumented */
++ /* SRC_COMB_CFG */
+ if (is_cx23888(state))
+ cx25840_write4(client, 0x454, 0x6628021F);
+ else
+@@ -1111,16 +1111,25 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
+ cx25840_write4(client, 0x410, 0xffff0dbf);
+ cx25840_write4(client, 0x414, 0x00137d03);
+
+- cx25840_write4(client, state->vbi_regs_offset + 0x42c, 0x42600000);
+- cx25840_write4(client, state->vbi_regs_offset + 0x430, 0x0000039b);
+- cx25840_write4(client, state->vbi_regs_offset + 0x438, 0x00000000);
+-
+- cx25840_write4(client, state->vbi_regs_offset + 0x440, 0xF8E3E824);
+- cx25840_write4(client, state->vbi_regs_offset + 0x444, 0x401040dc);
+- cx25840_write4(client, state->vbi_regs_offset + 0x448, 0xcd3f02a0);
+- cx25840_write4(client, state->vbi_regs_offset + 0x44c, 0x161f1000);
+- cx25840_write4(client, state->vbi_regs_offset + 0x450, 0x00000802);
+-
++ /* These are not VBI controls */
++ if (is_cx23888(state)) {
++ /* 888 MISC_TIM_CTRL */
++ cx25840_write4(client, 0x42c, 0x42600000);
++ /* 888 FIELD_COUNT */
++ cx25840_write4(client, 0x430, 0x0000039b);
++ /* 888 VSCALE_CTRL */
++ cx25840_write4(client, 0x438, 0x00000000);
++ /* 888 DFE_CTRL1 */
++ cx25840_write4(client, 0x440, 0xF8E3E824);
++ /* 888 DFE_CTRL2 */
++ cx25840_write4(client, 0x444, 0x401040dc);
++ /* 888 DFE_CTRL3 */
++ cx25840_write4(client, 0x448, 0xcd3f02a0);
++ /* 888 PLL_CTRL */
++ cx25840_write4(client, 0x44c, 0x161f1000);
++ /* 888 HTL_CTRL */
++ cx25840_write4(client, 0x450, 0x00000802);
++ }
+ cx25840_write4(client, 0x91c, 0x01000000);
+ cx25840_write4(client, 0x8e0, 0x03063870);
+ cx25840_write4(client, 0x8d4, 0x7FFF0024);
+@@ -1398,8 +1407,9 @@ static int cx25840_set_fmt(struct v4l2_subdev *sd,
+ if ((fmt->width == 0) || (Vlines == 0) ||
+ (fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) ||
+ (Vlines * 8 < Vsrc) || (Vsrc + 1 < Vlines)) {
+- v4l_err(client, "%dx%d is not a valid size!\n",
+- fmt->width, fmt->height);
++ v4l_err(client, "%dx%d is not a valid size! (Hsrc=%d, Vsrc=%d, Vlines=%d, is_50Hz=%u)\n",
++ fmt->width, fmt->height, Hsrc, Vsrc,
++ Vlines, is_50Hz);
+ return -ERANGE;
+ }
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+@@ -1727,6 +1737,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
+ if (is_cx2388x(state) || is_cx231xx(state))
+ return 0;
+
++ /* PIN_CTRL1 */
+ if (enable) {
+ v = cx25840_read(client, 0x115) | 0x0c;
+ cx25840_write(client, 0x115, v);
+diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
+index 28eab9c..9b6c1f1 100644
+--- a/drivers/media/pci/cx23885/cx23885-cards.c
++++ b/drivers/media/pci/cx23885/cx23885-cards.c
+@@ -325,8 +325,7 @@ struct cx23885_board cx23885_boards[] = {
+ .name = "Hauppauge WinTV-HVR1255",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+- .tuner_type = TUNER_ABSENT,
+- .tuner_addr = 0x42, /* 0x84 >> 1 */
++ .tuner_type = TUNER_NXP_TDA18271,
+ .force_bff = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+@@ -354,8 +353,7 @@ struct cx23885_board cx23885_boards[] = {
+ .name = "Hauppauge WinTV-HVR1255",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+- .tuner_type = TUNER_ABSENT,
+- .tuner_addr = 0x42, /* 0x84 >> 1 */
++ .tuner_type = TUNER_NXP_TDA18271,
+ .force_bff = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+@@ -767,14 +765,80 @@ struct cx23885_board cx23885_boards[] = {
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_QUADHD_DVB] = {
+- .name = "Hauppauge WinTV-QuadHD-DVB",
++ .name = "Hauppauge WinTV-QuadHD-DVB",
++ .porta = CX23885_ANALOG_VIDEO,
++ .portb = CX23885_MPEG_DVB,
++ .portc = CX23885_MPEG_DVB,
++ .tuner_type = TUNER_ABSENT,
++ .force_bff = 1,
++ .input = {{
++ .type = CX23885_VMUX_TELEVISION,
++ .vmux = CX25840_VIN7_CH3 |
++ CX25840_VIN5_CH2 |
++ CX25840_VIN2_CH1 |
++ CX25840_DIF_ON,
++ .amux = CX25840_AUDIO8,
++ } },
++ },
++ [CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885] = {
++ .name = "Hauppauge WinTV-QuadHD-DVB(885)",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
++ .tuner_type = TUNER_ABSENT,
+ },
+ [CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC] = {
+- .name = "Hauppauge WinTV-QuadHD-ATSC",
++ .name = "Hauppauge WinTV-QuadHD-ATSC",
++ .porta = CX23885_ANALOG_VIDEO,
++ .portb = CX23885_MPEG_DVB,
++ .portc = CX23885_MPEG_DVB,
++ .tuner_type = TUNER_ABSENT,
++ .force_bff = 1,
++ .input = {{
++ .type = CX23885_VMUX_TELEVISION,
++ .vmux = CX25840_VIN7_CH3 |
++ CX25840_VIN5_CH2 |
++ CX25840_VIN2_CH1 |
++ CX25840_DIF_ON,
++ .amux = CX25840_AUDIO8,
++ } },
++ },
++ [CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885] = {
++ .name = "Hauppauge WinTV-QuadHD-ATSC(885)",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
++ .tuner_type = TUNER_ABSENT,
++ },
++ [CX23885_BOARD_HAUPPAUGE_HVR1265_K4] = {
++ .name = "Hauppauge WinTV-HVR-1265(161111)",
++ .porta = CX23885_ANALOG_VIDEO,
++ .portc = CX23885_MPEG_DVB,
++ .tuner_type = TUNER_SILABS_SI2157,
++ .force_bff = 1,
++ .input = {{
++ .type = CX23885_VMUX_TELEVISION,
++ .vmux = CX25840_VIN7_CH3 |
++ CX25840_VIN5_CH2 |
++ CX25840_VIN2_CH1 |
++ CX25840_DIF_ON,
++ .amux = CX25840_AUDIO8,
++ }, {
++ .type = CX23885_VMUX_COMPOSITE1,
++ .vmux = CX25840_VIN7_CH3 |
++ CX25840_VIN4_CH2 |
++ CX25840_VIN6_CH1,
++ .amux = CX25840_AUDIO7,
++ }, {
++ .type = CX23885_VMUX_SVIDEO,
++ .vmux = CX25840_VIN7_CH3 |
++ CX25840_VIN4_CH2 |
++ CX25840_VIN8_CH1 |
++ CX25840_SVIDEO_ON,
++ .amux = CX25840_AUDIO7,
++ } },
++ },
++ [CX23885_BOARD_HAUPPAUGE_STARBURST2] = {
++ .name = "Hauppauge WinTV-Starburst2",
++ .portb = CX23885_MPEG_DVB,
+ },
+ };
+ const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
+@@ -1027,6 +1091,10 @@ struct cx23885_subid cx23885_subids[] = {
+ .subvendor = 0x0070,
+ .subdevice = 0x7133,
+ .card = CX23885_BOARD_HAUPPAUGE_IMPACTVCBE,
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x7137,
++ .card = CX23885_BOARD_HAUPPAUGE_IMPACTVCBE,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb98,
+@@ -1087,7 +1155,16 @@ struct cx23885_subid cx23885_subids[] = {
+ .subvendor = 0x0070,
+ .subdevice = 0x6b18,
+ .card = CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC, /* Tuner Pair 2 */
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x2a18,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1265_K4, /* Hauppauge WinTV HVR-1265 (Model 161xx1, Hybrid ATSC/QAM-B) */
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0xf02a,
++ .card = CX23885_BOARD_HAUPPAUGE_STARBURST2,
+ },
++
+ };
+ const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
+
+@@ -1287,25 +1364,28 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
+ case 150329:
+ /* WinTV-HVR5525 (PCIe, DVB-S/S2, DVB-T/T2/C) */
+ break;
+- case 166100:
++ case 161111:
++ /* WinTV-HVR-1265 (PCIe, Analog/ATSC/QAM-B) */
++ break;
++ case 166100: /* 888 version, hybrid */
++ case 166200: /* 885 version, DVB only */
+ /* WinTV-QuadHD (DVB) Tuner Pair 1 (PCIe, IR, half height,
+ DVB-T/T2/C, DVB-T/T2/C */
+ break;
+- case 166101:
++ case 166101: /* 888 version, hybrid */
++ case 166201: /* 885 version, DVB only */
+ /* WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height,
+ DVB-T/T2/C, DVB-T/T2/C */
+ break;
+- case 165100:
+- /*
+- * WinTV-QuadHD (ATSC) Tuner Pair 1 (PCIe, IR, half height,
+- * ATSC, ATSC
+- */
++ case 165100: /* 888 version, hybrid */
++ case 165200: /* 885 version, digital only */
++ /* WinTV-QuadHD (ATSC) Tuner Pair 1 (PCIe, IR, half height,
++ * ATSC/QAM-B, ATSC/QAM-B */
+ break;
+- case 165101:
+- /*
+- * WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height,
+- * ATSC, ATSC
+- */
++ case 165101: /* 888 version, hybrid */
++ case 165201: /* 885 version, digital only */
++ /* WinTV-QuadHD (ATSC) Tuner Pair 2 (PCIe, IR, half height,
++ * ATSC/QAM-B, ATSC/QAM-B */
+ break;
+ default:
+ pr_warn("%s: warning: unknown hauppauge model #%d\n",
+@@ -1778,8 +1858,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
+ cx23885_gpio_set(dev, GPIO_2);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR5525:
+- case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
+- case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
++ case CX23885_BOARD_HAUPPAUGE_STARBURST2:
+ /*
+ * HVR5525 GPIO Details:
+ * GPIO-00 IR_WIDE
+@@ -1809,6 +1888,22 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
+ * card does not have any GPIO's connected to subcomponents.
+ */
+ break;
++ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885:
++ /*
++ * GPIO-08 TER1_RESN
++ * GPIO-09 TER2_RESN
++ */
++ /* Put the parts into reset and back */
++ cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1);
++ cx23885_gpio_clear(dev, GPIO_8 | GPIO_9);
++ msleep(100);
++ cx23885_gpio_set(dev, GPIO_8 | GPIO_9);
++ msleep(100);
++ break;
+ }
+ }
+
+@@ -2054,8 +2149,13 @@ void cx23885_card_setup(struct cx23885_dev *dev)
+ case CX23885_BOARD_HAUPPAUGE_STARBURST:
+ case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE:
+ case CX23885_BOARD_HAUPPAUGE_HVR5525:
++ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
++ case CX23885_BOARD_HAUPPAUGE_STARBURST2:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885:
++
+ if (dev->i2c_bus[0].i2c_rc == 0)
+ hauppauge_eeprom(dev, eeprom+0xc0);
+ break;
+@@ -2194,6 +2294,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR5525:
++ case CX23885_BOARD_HAUPPAUGE_STARBURST2:
+ ts1->gen_ctrl_val = 0x5; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+@@ -2201,8 +2302,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
++ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885:
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885:
+ ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+@@ -2259,6 +2363,9 @@ void cx23885_card_setup(struct cx23885_dev *dev)
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
++ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_MYGICA_X8506:
+diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
+index 8f63df1..8afddd6 100644
+--- a/drivers/media/pci/cx23885/cx23885-core.c
++++ b/drivers/media/pci/cx23885/cx23885-core.c
+@@ -869,6 +869,14 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
+ cx23885_card_list(dev);
+ }
+
++ if (dev->pci->device == 0x8852) {
++ /* no DIF on cx23885, so no analog tuner support possible */
++ if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC)
++ dev->board = CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885;
++ else if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB)
++ dev->board = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885;
++ }
++
+ /* If the user specific a clk freq override, apply it */
+ if (cx23885_boards[dev->board].clk_freq > 0)
+ dev->clk_freq = cx23885_boards[dev->board].clk_freq;
+diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
+index 67ad041..53e59e4 100644
+--- a/drivers/media/pci/cx23885/cx23885-dvb.c
++++ b/drivers/media/pci/cx23885/cx23885-dvb.c
+@@ -930,6 +930,18 @@ static const struct m88ds3103_config hauppauge_hvr5525_m88ds3103_config = {
+ .agc = 0x99,
+ };
+
++static struct lgdt3306a_config hauppauge_hvr1265k4_config = {
++ .i2c_addr = 0x59,
++ .qam_if_khz = 4000,
++ .vsb_if_khz = 3250,
++ .deny_i2c_rptr = 1, /* Disabled */
++ .spectral_inversion = 0, /* Disabled */
++ .mpeg_mode = LGDT3306A_MPEG_SERIAL,
++ .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE,
++ .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH,
++ .xtalMHz = 25, /* 24 or 25 */
++};
++
+ static int netup_altera_fpga_rw(void *device, int flag, int data, int read)
+ {
+ struct cx23885_dev *dev = (struct cx23885_dev *)device;
+@@ -2217,10 +2229,15 @@ static int dvb_register(struct cx23885_tsport *port)
+ }
+ port->i2c_client_tuner = client_tuner;
+ break;
+- case CX23885_BOARD_HAUPPAUGE_HVR5525: {
++ case CX23885_BOARD_HAUPPAUGE_STARBURST2:
++ case CX23885_BOARD_HAUPPAUGE_HVR5525:
++ i2c_bus = &dev->i2c_bus[0];
++ i2c_bus2 = &dev->i2c_bus[1];
++
+ struct m88rs6000t_config m88rs6000t_config;
+ struct a8293_platform_data a8293_pdata = {};
+
++ pr_info("%s(): port=%d\n", __func__, port->nr);
+ switch (port->nr) {
+
+ /* port b - satellite */
+@@ -2228,7 +2245,7 @@ static int dvb_register(struct cx23885_tsport *port)
+ /* attach frontend */
+ fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+ &hauppauge_hvr5525_m88ds3103_config,
+- &dev->i2c_bus[0].i2c_adap, &adapter);
++ &i2c_bus->i2c_adap, &adapter);
+ if (fe0->dvb.frontend == NULL)
+ break;
+
+@@ -2239,7 +2256,7 @@ static int dvb_register(struct cx23885_tsport *port)
+ info.addr = 0x0b;
+ info.platform_data = &a8293_pdata;
+ request_module("a8293");
+- client_sec = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info);
++ client_sec = i2c_new_device(&i2c_bus->i2c_adap, &info);
+ if (!client_sec || !client_sec->dev.driver)
+ goto frontend_detach;
+ if (!try_module_get(client_sec->dev.driver->owner)) {
+@@ -2281,7 +2298,7 @@ static int dvb_register(struct cx23885_tsport *port)
+ info.addr = 0x64;
+ info.platform_data = &si2168_config;
+ request_module("%s", info.type);
+- client_demod = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info);
++ client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info);
+ if (!client_demod || !client_demod->dev.driver)
+ goto frontend_detach;
+ if (!try_module_get(client_demod->dev.driver->owner)) {
+@@ -2299,7 +2316,7 @@ static int dvb_register(struct cx23885_tsport *port)
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+ request_module("%s", info.type);
+- client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info);
++ client_tuner = i2c_new_device(&i2c_bus2->i2c_adap, &info);
+ if (!client_tuner || !client_tuner->dev.driver) {
+ module_put(client_demod->dev.driver->owner);
+ i2c_unregister_device(client_demod);
+@@ -2317,8 +2334,10 @@ static int dvb_register(struct cx23885_tsport *port)
+ break;
+ }
+ break;
+- }
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885:
++ pr_info("%s(): board=%d port=%d\n", __func__,
++ dev->board, port->nr);
+ switch (port->nr) {
+ /* port b - Terrestrial/cable */
+ case 1:
+@@ -2365,6 +2384,16 @@ static int dvb_register(struct cx23885_tsport *port)
+ goto frontend_detach;
+ }
+ port->i2c_client_tuner = client_tuner;
++
++ /* we only attach tuner for analog on the 888 version */
++ if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) {
++ pr_info("%s(): QUADHD_DVB analog setup\n",
++ __func__);
++ dev->ts1.analog_fe.tuner_priv = client_tuner;
++ dvb_attach(si2157_attach, &dev->ts1.analog_fe,
++ info.addr, &dev->i2c_bus[1].i2c_adap,
++ &si2157_config);
++ }
+ break;
+
+ /* port c - terrestrial/cable */
+@@ -2416,6 +2445,9 @@ static int dvb_register(struct cx23885_tsport *port)
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885:
++ pr_info("%s(): board=%d port=%d\n", __func__,
++ dev->board, port->nr);
+ switch (port->nr) {
+ /* port b - Terrestrial/cable */
+ case 1:
+@@ -2451,6 +2483,16 @@ static int dvb_register(struct cx23885_tsport *port)
+ goto frontend_detach;
+ }
+ port->i2c_client_tuner = client_tuner;
++
++ /* we only attach tuner for analog on the 888 version */
++ if (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC) {
++ pr_info("%s(): QUADHD_ATSC analog setup\n",
++ __func__);
++ dev->ts1.analog_fe.tuner_priv = client_tuner;
++ dvb_attach(si2157_attach, &dev->ts1.analog_fe,
++ info.addr, &dev->i2c_bus[1].i2c_adap,
++ &si2157_config);
++ }
+ break;
+
+ /* port c - terrestrial/cable */
+@@ -2490,6 +2532,47 @@ static int dvb_register(struct cx23885_tsport *port)
+ break;
+ }
+ break;
++ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
++ pr_info("%s(): port=%d\n", __func__, port->nr);
++ switch (port->nr) {
++ /* port c - Terrestrial/cable */
++ case 2:
++ /* attach frontend */
++ i2c_bus = &dev->i2c_bus[0];
++ fe0->dvb.frontend = dvb_attach(lgdt3306a_attach,
++ &hauppauge_hvr1265k4_config, &i2c_bus->i2c_adap);
++ if (fe0->dvb.frontend == NULL)
++ break;
++
++ /* attach tuner */
++ si2157_config.fe = fe0->dvb.frontend;
++ si2157_config.if_port = 1;
++ si2157_config.inversion = 1;
++ memset(&info, 0, sizeof(struct i2c_board_info));
++ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
++ info.addr = 0x60;
++ info.platform_data = &si2157_config;
++ request_module("%s", info.type);
++ client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info);
++ if (!client_tuner || !client_tuner->dev.driver) {
++ goto frontend_detach;
++ }
++ if (!try_module_get(client_tuner->dev.driver->owner)) {
++ i2c_unregister_device(client_tuner);
++ client_tuner = NULL;
++ goto frontend_detach;
++ }
++ port->i2c_client_tuner = client_tuner;
++
++ dev->ts1.analog_fe.tuner_priv = client_tuner;
++ dvb_attach(si2157_attach, &dev->ts1.analog_fe,
++ 0x60, &dev->i2c_bus[1].i2c_adap,
++ &si2157_config);
++ pr_info("%s(): HVR1265_K4 setup\n", __func__);
++ break;
++ }
++ break;
++
+
+ default:
+ pr_info("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
+diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
+index 0f4e542..be49589 100644
+--- a/drivers/media/pci/cx23885/cx23885-input.c
++++ b/drivers/media/pci/cx23885/cx23885-input.c
+@@ -94,6 +94,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
++ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
+ /*
+ * The only boards we handle right now. However other boards
+ * using the CX2388x integrated IR controller should be similar
+@@ -153,6 +154,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
++ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
+ /*
+ * The IR controller on this board only returns pulse widths.
+ * Any other mode setting will fail to set up the device.
+@@ -283,6 +285,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
++ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
+ /* Integrated CX2388[58] IR controller */
+ allowed_protos = RC_PROTO_BIT_ALL_IR_DECODER;
+ /* The grey Hauppauge RC-5 remote */
+diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
+index ecc580a..afa383f 100644
+--- a/drivers/media/pci/cx23885/cx23885-video.c
++++ b/drivers/media/pci/cx23885/cx23885-video.c
+@@ -263,6 +263,9 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
+ (dev->board == CX23885_BOARD_HAUPPAUGE_IMPACTVCBE) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
++ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4) ||
++ (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC) ||
++ (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
+ (dev->board == CX23885_BOARD_MYGICA_X8507) ||
+ (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) ||
+@@ -993,7 +996,10 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
+
+ if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
+- (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111))
++ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
++ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1265_K4) ||
++ (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_DVB) ||
++ (dev->board == CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC))
+ fe = &dev->ts1.analog_fe;
+
+ if (fe && fe->ops.tuner_ops.set_analog_params) {
+@@ -1022,7 +1028,10 @@ int cx23885_set_frequency(struct file *file, void *priv,
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
++ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
++ case CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC:
+ ret = cx23885_set_freq_via_ops(dev, f);
+ break;
+ default:
+diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
+index 6aab713..6e659be 100644
+--- a/drivers/media/pci/cx23885/cx23885.h
++++ b/drivers/media/pci/cx23885/cx23885.h
+@@ -107,6 +107,10 @@
+ #define CX23885_BOARD_VIEWCAST_460E 55
+ #define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB 56
+ #define CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC 57
++#define CX23885_BOARD_HAUPPAUGE_HVR1265_K4 58
++#define CX23885_BOARD_HAUPPAUGE_STARBURST2 59
++#define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885 60
++#define CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885 61
+
+ #define GPIO_0 0x00000001
+ #define GPIO_1 0x00000002
+diff --git a/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c
+index e76d3ba..9522c6c 100644
+--- a/drivers/media/pci/saa7164/saa7164-dvb.c
++++ b/drivers/media/pci/saa7164/saa7164-dvb.c
+@@ -110,8 +110,9 @@ static struct si2157_config hauppauge_hvr2255_tuner_config = {
+ .if_port = 1,
+ };
+
+-static int si2157_attach(struct saa7164_port *port, struct i2c_adapter *adapter,
+- struct dvb_frontend *fe, u8 addr8bit, struct si2157_config *cfg)
++static int si2157_attach_priv(struct saa7164_port *port,
++ struct i2c_adapter *adapter, struct dvb_frontend *fe,
++ u8 addr8bit, struct si2157_config *cfg)
+ {
+ struct i2c_board_info bi;
+ struct i2c_client *tuner;
+@@ -624,11 +625,13 @@ int saa7164_dvb_register(struct saa7164_port *port)
+ if (port->dvb.frontend != NULL) {
+
+ if (port->nr == 0) {
+- si2157_attach(port, &dev->i2c_bus[0].i2c_adap,
++ si2157_attach_priv(port,
++ &dev->i2c_bus[0].i2c_adap,
+ port->dvb.frontend, 0xc0,
+ &hauppauge_hvr2255_tuner_config);
+ } else {
+- si2157_attach(port, &dev->i2c_bus[1].i2c_adap,
++ si2157_attach_priv(port,
++ &dev->i2c_bus[1].i2c_adap,
+ port->dvb.frontend, 0xc0,
+ &hauppauge_hvr2255_tuner_config);
+ }
+diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
+index e35b1fa..2f79bdb 100644
+--- a/drivers/media/tuners/si2157.c
++++ b/drivers/media/tuners/si2157.c
+@@ -1,5 +1,5 @@
+ /*
+- * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver
++ * Silicon Labs Si2141/2146/2147/2148/2157/2158 silicon tuner driver
+ *
+ * Copyright (C) 2014 Antti Palosaari
+ *
+@@ -18,6 +18,11 @@
+
+ static const struct dvb_tuner_ops si2157_ops;
+
++static DEFINE_MUTEX(si2157_list_mutex);
++static LIST_HEAD(hybrid_tuner_instance_list);
++
++/*---------------------------------------------------------------------*/
++
+ /* execute firmware command */
+ static int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd)
+ {
+@@ -52,18 +57,25 @@ static int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd)
+ }
+
+ /* firmware ready? */
+- if ((cmd->args[0] >> 7) & 0x01)
++ if (cmd->args[0] & 0x80)
+ break;
++ usleep_range(5000, 10000);
+ }
+
+- dev_dbg(&client->dev, "cmd execution took %d ms\n",
++ dev_dbg(&client->dev, "cmd execution took %d ms, status=%x\n",
+ jiffies_to_msecs(jiffies) -
+- (jiffies_to_msecs(timeout) - TIMEOUT));
++ (jiffies_to_msecs(timeout) - TIMEOUT),
++ cmd->args[0]);
+
+- if (!((cmd->args[0] >> 7) & 0x01)) {
++ if (!(cmd->args[0] & 0x80)) {
+ ret = -ETIMEDOUT;
+ goto err_mutex_unlock;
+ }
++ /* check error status bit */
++ if (cmd->args[0] & 0x40) {
++ ret = -EAGAIN;
++ goto err_mutex_unlock;
++ }
+ }
+
+ mutex_unlock(&dev->i2c_mutex);
+@@ -84,24 +96,25 @@ static int si2157_init(struct dvb_frontend *fe)
+ struct si2157_cmd cmd;
+ const struct firmware *fw;
+ const char *fw_name;
+- unsigned int uitmp, chip_id;
++ unsigned int chip_id, xtal_trim;
+
+ dev_dbg(&client->dev, "\n");
+
+- /* Returned IF frequency is garbage when firmware is not running */
+- memcpy(cmd.args, "\x15\x00\x06\x07", 4);
++ /* Try to get Xtal trim property setting, to verify tuner still running;
++ * replaces previous test of IF freq
++ */
++ memcpy(cmd.args, "\x15\x00\x04\x02", 4);
+ cmd.wlen = 4;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+- if (ret)
+- goto err;
+
+- uitmp = cmd.args[2] << 0 | cmd.args[3] << 8;
+- dev_dbg(&client->dev, "if_frequency kHz=%u\n", uitmp);
++ xtal_trim = cmd.args[2] | (cmd.args[3] << 8);
+
+- if (uitmp == dev->if_frequency / 1000)
++ if ((ret == 0) && (xtal_trim < 16))
+ goto warm;
+
++ dev->if_frequency = 0; /* we no longer know current tuner state */
++
+ /* power up */
+ if (dev->chiptype == SI2157_CHIPTYPE_SI2146) {
+ memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9);
+@@ -230,6 +243,45 @@ skip_fw_download:
+
+ dev_info(&client->dev, "firmware version: %c.%c.%d\n",
+ cmd.args[6], cmd.args[7], cmd.args[8]);
++
++ if (dev->chiptype == SI2157_CHIPTYPE_SI2141) {
++ /* set clock */
++ memcpy(cmd.args, "\xc0\x00\x0d", 3);
++ cmd.wlen = 3;
++ cmd.rlen = 1;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++ /* setup PIN */
++ memcpy(cmd.args, "\x12\x80\x80\x85\x00\x81\x00", 7);
++ cmd.wlen = 7;
++ cmd.rlen = 7;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++ }
++
++ /* enable tuner status flags, for si2157_tune_wait() */
++ memcpy(cmd.args, "\x14\x00\x01\x05\x01\x00", 6);
++ cmd.wlen = 6;
++ cmd.rlen = 1;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++
++ memcpy(cmd.args, "\x14\x00\x01\x06\x01\x00", 6);
++ cmd.wlen = 6;
++ cmd.rlen = 1;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++
++ memcpy(cmd.args, "\x14\x00\x01\x07\x01\x00", 6);
++ cmd.wlen = 6;
++ cmd.rlen = 1;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
+ warm:
+ /* init statistics in order signal app which are supported */
+ c->strength.len = 1;
+@@ -250,7 +302,7 @@ static int si2157_sleep(struct dvb_frontend *fe)
+ {
+ struct i2c_client *client = fe->tuner_priv;
+ struct si2157_dev *dev = i2c_get_clientdata(client);
+- int ret;
++ int ret = 0;
+ struct si2157_cmd cmd;
+
+ dev_dbg(&client->dev, "\n");
+@@ -274,6 +326,84 @@ err:
+ return ret;
+ }
+
++static int si2157_tune_wait(struct i2c_client *client, u8 is_digital)
++{
++#define TUN_TIMEOUT 40
++#define DIG_TIMEOUT 30
++#define ANALOG_TIMEOUT 150
++ struct si2157_dev *dev = i2c_get_clientdata(client);
++ int ret;
++ unsigned long timeout;
++ unsigned long start_time;
++ u8 wait_status;
++ u8 tune_lock_mask;
++
++ if (is_digital)
++ tune_lock_mask = 0x04;
++ else
++ tune_lock_mask = 0x02;
++
++ mutex_lock(&dev->i2c_mutex);
++
++ /* wait tuner command complete */
++ start_time = jiffies;
++ timeout = start_time + msecs_to_jiffies(TUN_TIMEOUT);
++ while (!time_after(jiffies, timeout)) {
++ ret = i2c_master_recv(client, &wait_status,
++ sizeof(wait_status));
++ if (ret < 0) {
++ goto err_mutex_unlock;
++ } else if (ret != sizeof(wait_status)) {
++ ret = -EREMOTEIO;
++ goto err_mutex_unlock;
++ }
++
++ /* tuner done? */
++ if ((wait_status & 0x81) == 0x81)
++ break;
++ usleep_range(5000, 10000);
++ }
++ /* if we tuned ok, wait a bit for tuner lock */
++ if ((wait_status & 0x81) == 0x81) {
++ if (is_digital)
++ timeout = jiffies + msecs_to_jiffies(DIG_TIMEOUT);
++ else
++ timeout = jiffies + msecs_to_jiffies(ANALOG_TIMEOUT);
++ while (!time_after(jiffies, timeout)) {
++ ret = i2c_master_recv(client, &wait_status,
++ sizeof(wait_status));
++ if (ret < 0) {
++ goto err_mutex_unlock;
++ } else if (ret != sizeof(wait_status)) {
++ ret = -EREMOTEIO;
++ goto err_mutex_unlock;
++ }
++
++ /* tuner locked? */
++ if (wait_status & tune_lock_mask)
++ break;
++ usleep_range(5000, 10000);
++ }
++ }
++
++ dev_dbg(&client->dev, "tuning took %d ms, status=0x%x\n",
++ jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_time),
++ wait_status);
++
++ if ((wait_status & 0xc0) != 0x80) {
++ ret = -ETIMEDOUT;
++ goto err_mutex_unlock;
++ }
++
++ mutex_unlock(&dev->i2c_mutex);
++ return 0;
++
++err_mutex_unlock:
++ mutex_unlock(&dev->i2c_mutex);
++ dev_dbg(&client->dev, "failed=%d\n", ret);
++ return ret;
++}
++
+ static int si2157_set_params(struct dvb_frontend *fe)
+ {
+ struct i2c_client *client = fe->tuner_priv;
+@@ -344,7 +474,7 @@ static int si2157_set_params(struct dvb_frontend *fe)
+ if (ret)
+ goto err;
+
+- /* set if frequency if needed */
++ /* set digital 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;
+@@ -358,7 +488,7 @@ static int si2157_set_params(struct dvb_frontend *fe)
+ dev->if_frequency = if_frequency;
+ }
+
+- /* set frequency */
++ /* set digital frequency */
+ memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8);
+ cmd.args[4] = (c->frequency >> 0) & 0xff;
+ cmd.args[5] = (c->frequency >> 8) & 0xff;
+@@ -370,24 +500,319 @@ static int si2157_set_params(struct dvb_frontend *fe)
+ if (ret)
+ goto err;
+
++ /* wait for tuning to complete, ignore any errors */
++ si2157_tune_wait(client, 1);
++
++ dev->bandwidth = bandwidth;
++ dev->frequency = c->frequency;
++
++ return 0;
++err:
++ dev->bandwidth = 0;
++ dev->frequency = 0;
++ dev->if_frequency = 0;
++ dev_dbg(&client->dev, "failed=%d\n", ret);
++ return ret;
++}
++
++static int si2157_set_analog_params(struct dvb_frontend *fe,
++ struct analog_parameters *params)
++{
++ struct i2c_client *client = fe->tuner_priv;
++ struct si2157_dev *dev = i2c_get_clientdata(client);
++ char *std; /* for debugging */
++ int ret;
++ struct si2157_cmd cmd;
++ u32 bandwidth = 0;
++ u32 if_frequency = 0;
++ u32 freq = 0;
++ u64 tmp_lval = 0;
++ u8 system = 0;
++ u8 color = 0; /* 0=NTSC/PAL, 0x10=SECAM */
++ u8 invert_analog = 1; /* analog tuner spectrum; 0=normal, 1=inverted */
++
++ if (dev->chiptype != SI2157_CHIPTYPE_SI2157) {
++ dev_info(&client->dev, "%s: Analog tuning not supported for chiptype=%u\n",
++ __func__, dev->chiptype);
++ ret = -EINVAL;
++ goto err;
++ }
++
++ if (!dev->active)
++ si2157_init(fe);
++
++ if (!dev->active) {
++ ret = -EAGAIN;
++ goto err;
++ }
++ if (params->mode == V4L2_TUNER_RADIO) {
++ /*
++ * std = "fm";
++ * bandwidth = 1700000; //best can do for FM, AGC will be a mess though
++ * if_frequency = 1250000; //HVR-225x(saa7164), HVR-12xx(cx23885)
++ * if_frequency = 6600000; //HVR-9xx(cx231xx)
++ * if_frequency = 5500000; //HVR-19xx(pvrusb2)
++ */
++ dev_dbg(&client->dev, "si2157 does not currently support FM radio\n");
++ ret = -EINVAL;
++ goto err;
++ }
++ tmp_lval = params->frequency * 625LL;
++ do_div(tmp_lval, 10); /* convert to HZ */
++ freq = (u32)tmp_lval;
++
++ if (freq < 1000000) /* is freq in KHz */
++ freq = freq * 1000;
++ dev->frequency = freq;
++
++ if (params->std & (V4L2_STD_B|V4L2_STD_GH)) {
++ if (freq >= 470000000) {
++ std = "palGH";
++ bandwidth = 8000000;
++ if_frequency = 6000000; /* matches tda18271C2, works w/cx23885 */
++ system = 1;
++ if (params->std & (V4L2_STD_SECAM_G|V4L2_STD_SECAM_H)) {
++ std = "secamGH";
++ color = 0x10;
++ }
++ } else {
++ std = "palB";
++ bandwidth = 7000000;
++ if_frequency = 6000000; /* matches tda18271C2, works w/cx23885 */;
++ system = 0;
++ if (params->std & V4L2_STD_SECAM_B) {
++ std = "secamB";
++ color = 0x10;
++ }
++ }
++ } else if (params->std & V4L2_STD_MN) {
++ std = "MN";
++ bandwidth = 6000000;
++ if_frequency = 5400000; /* matches tda18271C2, works w/cx23885 */
++ system = 2;
++ } else if (params->std & V4L2_STD_PAL_I) {
++ std = "palI";
++ bandwidth = 8000000;
++ if_frequency = 7250000; /* matches tda18271C2, does not work yet w/cx23885 */
++ system = 4;
++ } else if (params->std & V4L2_STD_DK) {
++ std = "palDK";
++ bandwidth = 8000000;
++ if_frequency = 6900000; /* matches tda18271C2, does not work yet w/cx23885 */
++ system = 5;
++ if (params->std & V4L2_STD_SECAM_DK) {
++ std = "secamDK";
++ color = 0x10;
++ }
++ } else if (params->std & V4L2_STD_SECAM_L) {
++ std = "secamL";
++ bandwidth = 8000000;
++ if_frequency = 6750000; /* not tested yet w/cx23885 */
++ system = 6; color = 0x10;
++ } else if (params->std & V4L2_STD_SECAM_LC) {
++ std = "secamL'";
++ bandwidth = 7000000;
++ if_frequency = 1250000; /* not tested yet w/cx23885 */
++ system = 7; color = 0x10;
++ } else {
++ std = "unknown";
++ }
++ /* calc channel center freq */
++ freq = freq - 1250000 + (bandwidth/2);
++
++ dev_dbg(&client->dev,
++ "mode=%d system=%u std='%s' params->frequency=%u center freq=%u if=%u bandwidth=%u\n",
++ params->mode, system, std, params->frequency,
++ freq, if_frequency, bandwidth);
++
++ /* set analog IF port */
++ memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6);
++ /* in using dev->if_port, we assume analog and digital IF's */
++ /* are always on different ports */
++ /* assumes if_port definition is 0 or 1 for digital out */
++ cmd.args[4] = (dev->if_port == 1)?8:10;
++ cmd.args[5] = (dev->if_port == 1)?2:1; /* Analog AGC assumed external */
++ cmd.wlen = 6;
++ cmd.rlen = 4;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++
++ /* set analog IF output config */
++ memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6);
++ cmd.wlen = 6;
++ cmd.rlen = 4;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++
++ /* make this distinct from a digital IF */
++ dev->if_frequency = if_frequency | 1;
++
++ /* calc and set tuner analog if center frequency */
++ if_frequency = if_frequency + 1250000 - (bandwidth/2);
++ dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency);
++
++ memcpy(cmd.args, "\x14\x00\x0C\x06", 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;
++
++ /* set analog AGC config */
++ memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6);
++ cmd.wlen = 6;
++ cmd.rlen = 4;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++
++ /* set analog video mode */
++ memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
++ cmd.args[4] = system | color;
++#if 1 /* can use dev->inversion if assumed it applies to both digital/analog */
++ if (invert_analog)
++ cmd.args[5] |= 0x02;
++#else
++ if (dev->inversion)
++ cmd.args[5] |= 0x02;
++#endif
++ cmd.wlen = 6;
++ cmd.rlen = 1;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++
++ /* set analog frequency */
++ memcpy(cmd.args, "\x41\x01\x00\x00\x00\x00\x00\x00", 8);
++ cmd.args[4] = (freq >> 0) & 0xff;
++ cmd.args[5] = (freq >> 8) & 0xff;
++ cmd.args[6] = (freq >> 16) & 0xff;
++ cmd.args[7] = (freq >> 24) & 0xff;
++ cmd.wlen = 8;
++ cmd.rlen = 1;
++ ret = si2157_cmd_execute(client, &cmd);
++ if (ret)
++ goto err;
++
++ /* wait for tuning to complete, ignore any errors */
++ si2157_tune_wait(client, 0);
++
++#if 1 /* testing */
++ /* get tuner status, RSSI values */
++ memcpy(cmd.args, "\x42\x01", 2);
++ cmd.wlen = 2;
++ cmd.rlen = 12;
++ ret = si2157_cmd_execute(client, &cmd);
++
++ dev_info(&client->dev, "%s: tuner status: ret=%d rssi=%d mode=%x freq=%d\n",
++ __func__, ret, cmd.args[3], cmd.args[8],
++ (cmd.args[7]<<24 | cmd.args[6]<<16 |
++ cmd.args[5]<<8 | cmd.args[4]));
++#endif
++
++ dev->bandwidth = bandwidth;
++
+ return 0;
+ err:
++ dev->bandwidth = 0;
++ dev->frequency = 0;
++ dev->if_frequency = 0;
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+ }
+
++static int si2157_get_frequency(struct dvb_frontend *fe, u32 *frequency)
++{
++ struct i2c_client *client = fe->tuner_priv;
++ struct si2157_dev *dev = i2c_get_clientdata(client);
++
++ *frequency = dev->frequency;
++ dev_info(&client->dev, "%s: freq=%u\n", __func__, dev->frequency);
++ return 0;
++}
++
++static int si2157_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
++{
++ struct i2c_client *client = fe->tuner_priv;
++ struct si2157_dev *dev = i2c_get_clientdata(client);
++
++ *bandwidth = dev->bandwidth;
++ dev_info(&client->dev, "%s: bandwidth=%u\n", __func__, dev->bandwidth);
++ return 0;
++}
++
+ static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+ {
+ struct i2c_client *client = fe->tuner_priv;
+ struct si2157_dev *dev = i2c_get_clientdata(client);
+
+- *frequency = dev->if_frequency;
++ *frequency = dev->if_frequency & ~1; /* strip analog IF indicator bit */
++ dev_info(&client->dev, "%s: if_frequency=%u\n", __func__, *frequency);
++ return 0;
++}
++
++static void si2157_release(struct dvb_frontend *fe)
++{
++ struct i2c_client *client = fe->tuner_priv;
++ struct si2157_dev *dev = NULL;
++
++ pr_info("%s: client=%p\n", __func__, client);
++ if (client == NULL)
++ return;
++
++ dev = i2c_get_clientdata(client);
++ pr_info("%s: dev=%p\n", __func__, dev);
++ if (dev == NULL)
++ return;
++
++ /* only remove dev reference from final instance */
++ if (hybrid_tuner_report_instance_count(dev) == 1)
++ i2c_set_clientdata(client, NULL);
++
++ mutex_lock(&si2157_list_mutex);
++ hybrid_tuner_release_state(dev);
++ mutex_unlock(&si2157_list_mutex);
++
++ fe->tuner_priv = NULL;
++}
++
++static int si2157_setup_configuration(struct dvb_frontend *fe,
++ struct si2157_config *cfg)
++{
++ struct i2c_client *client = fe->tuner_priv;
++ struct si2157_dev *dev = NULL;
++
++ pr_info("%s: client=%p\n", __func__, client);
++ if (client == NULL)
++ return -1;
++
++ dev = i2c_get_clientdata(client);
++ pr_info("%s: dev=%p\n", __func__, dev);
++ if (dev == NULL)
++ return -1;
++
++ if (cfg) {
++ pr_info("%s(0x%02X): dvb driver submitted configuration; port=%d invert=%d\n",
++ __func__, dev->addr,
++ cfg->if_port, cfg->inversion);
++ dev->inversion = cfg->inversion;
++ dev->if_port = cfg->if_port;
++ } else {
++ pr_info("%s(0x%02X): default configuration\n",
++ __func__, dev->addr);
++ dev->inversion = true;
++ dev->if_port = 1;
++ }
+ return 0;
+ }
+
+ static const struct dvb_tuner_ops si2157_ops = {
+ .info = {
+- .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158",
++ .name = "Silicon Labs Si2141/2146/2147/2148/2157/2158",
+ .frequency_min = 42000000,
+ .frequency_max = 870000000,
+ },
+@@ -395,6 +820,10 @@ static const struct dvb_tuner_ops si2157_ops = {
+ .init = si2157_init,
+ .sleep = si2157_sleep,
+ .set_params = si2157_set_params,
++ .set_analog_params = si2157_set_analog_params,
++ .release = si2157_release,
++ .get_frequency = si2157_get_frequency,
++ .get_bandwidth = si2157_get_bandwidth,
+ .get_if_frequency = si2157_get_if_frequency,
+ };
+
+@@ -417,7 +846,7 @@ static void si2157_stat_work(struct work_struct *work)
+ goto err;
+
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+- c->strength.stat[0].svalue = (s8) cmd.args[3] * 1000;
++ c->strength.stat[0].svalue = ((s8) cmd.args[3]) * 1000;
+
+ schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
+ return;
+@@ -431,60 +860,23 @@ static int si2157_probe(struct i2c_client *client,
+ {
+ struct si2157_config *cfg = client->dev.platform_data;
+ struct dvb_frontend *fe = cfg->fe;
+- struct si2157_dev *dev;
+- struct si2157_cmd cmd;
+- int ret;
++ struct si2157_dev *dev = NULL;
++ unsigned short addr = client->addr;
++ int ret = 0;
++
++ pr_info("%s: probing si2157 tuner fe=%p cfg=%p addr=0X%2x\n",
++ __func__, fe, cfg, addr);
++ fe->tuner_priv = client;
++ si2157_setup_configuration(fe, cfg);
+
+- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+- if (!dev) {
+- ret = -ENOMEM;
+- dev_err(&client->dev, "kzalloc() failed\n");
++ if (!dvb_attach(si2157_attach, fe, (u8)addr, client->adapter, cfg)) {
++ dev_err(&client->dev, "%s: attaching si2157 tuner failed\n",
++ __func__);
+ goto err;
+ }
+
+- i2c_set_clientdata(client, dev);
+- dev->fe = cfg->fe;
+- dev->inversion = cfg->inversion;
+- dev->if_port = cfg->if_port;
++ dev = i2c_get_clientdata(client);
+ dev->chiptype = (u8)id->driver_data;
+- dev->if_frequency = 5000000; /* default value of property 0x0706 */
+- mutex_init(&dev->i2c_mutex);
+- INIT_DELAYED_WORK(&dev->stat_work, si2157_stat_work);
+-
+- /* check if the tuner is there */
+- cmd.wlen = 0;
+- cmd.rlen = 1;
+- ret = si2157_cmd_execute(client, &cmd);
+- if (ret)
+- goto err_kfree;
+-
+- memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops));
+- fe->tuner_priv = client;
+-
+-#ifdef CONFIG_MEDIA_CONTROLLER
+- if (cfg->mdev) {
+- dev->mdev = cfg->mdev;
+-
+- dev->ent.name = KBUILD_MODNAME;
+- dev->ent.function = MEDIA_ENT_F_TUNER;
+-
+- dev->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
+- dev->pad[TUNER_PAD_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
+- dev->pad[TUNER_PAD_AUD_OUT].flags = MEDIA_PAD_FL_SOURCE;
+-
+- ret = media_entity_pads_init(&dev->ent, TUNER_NUM_PADS,
+- &dev->pad[0]);
+-
+- if (ret)
+- goto err_kfree;
+-
+- ret = media_device_register_entity(cfg->mdev, &dev->ent);
+- if (ret) {
+- media_entity_cleanup(&dev->ent);
+- goto err_kfree;
+- }
+- }
+-#endif
+
+ dev_info(&client->dev, "Silicon Labs %s successfully attached\n",
+ dev->chiptype == SI2157_CHIPTYPE_SI2141 ? "Si2141" :
+@@ -493,8 +885,6 @@ static int si2157_probe(struct i2c_client *client,
+
+ return 0;
+
+-err_kfree:
+- kfree(dev);
+ err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+@@ -503,7 +893,13 @@ err:
+ static int si2157_remove(struct i2c_client *client)
+ {
+ struct si2157_dev *dev = i2c_get_clientdata(client);
+- struct dvb_frontend *fe = dev->fe;
++ struct dvb_frontend *fe = NULL;
++
++ if (dev == NULL) {
++ pr_info("dev is NULL\n");
++ return 0;
++ }
++ fe = dev->fe;
+
+ dev_dbg(&client->dev, "\n");
+
+@@ -516,8 +912,7 @@ static int si2157_remove(struct i2c_client *client)
+ #endif
+
+ memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
+- fe->tuner_priv = NULL;
+- kfree(dev);
++ si2157_release(fe);
+
+ return 0;
+ }
+@@ -542,7 +937,129 @@ static struct i2c_driver si2157_driver = {
+
+ module_i2c_driver(si2157_driver);
+
+-MODULE_DESCRIPTION("Silicon Labs Si2141/Si2146/2147/2148/2157/2158 silicon tuner driver");
++struct dvb_frontend *si2157_attach(struct dvb_frontend *fe, u8 addr,
++ struct i2c_adapter *i2c,
++ struct si2157_config *cfg)
++{
++ struct i2c_client *client = NULL;
++ struct si2157_dev *dev = NULL;
++ struct si2157_cmd cmd;
++ int instance = 0, ret;
++
++ pr_info("%s (%d-%04x)\n", __func__,
++ i2c ? i2c_adapter_id(i2c) : 0,
++ addr);
++
++ mutex_lock(&si2157_list_mutex);
++
++ if (!cfg) {
++ pr_info("no configuration submitted\n");
++ goto fail;
++ }
++
++ if (!fe) {
++ pr_info("fe is NULL\n");
++ goto fail;
++ }
++
++ client = fe->tuner_priv;
++ if (!client) {
++ pr_info("client is NULL\n");
++ goto fail;
++ }
++
++ instance = hybrid_tuner_request_state(struct si2157_dev, dev,
++ hybrid_tuner_instance_list,
++ i2c, addr, "si2157");
++
++ pr_info("%s: instance=%d\n", __func__, instance);
++ switch (instance) {
++ case 0:
++ goto fail;
++ case 1:
++ /* new tuner instance */
++ pr_info("%s(): new instance for tuner @0x%02x\n",
++ __func__, addr);
++ dev->addr = addr;
++ i2c_set_clientdata(client, dev);
++
++ si2157_setup_configuration(fe, cfg);
++
++ dev->fe = fe;
++ /* BUGBUG - should chiptype come from config? */
++ dev->chiptype = (u8)SI2157_CHIPTYPE_SI2157;
++ dev->if_frequency = 0;
++
++ mutex_init(&dev->i2c_mutex);
++ INIT_DELAYED_WORK(&dev->stat_work, si2157_stat_work);
++
++ break;
++ default:
++ /* existing tuner instance */
++ pr_info("%s(): using existing instance for tuner @0x%02x\n",
++ __func__, addr);
++
++ /* allow dvb driver to override configuration settings */
++ if (cfg)
++ si2157_setup_configuration(fe, cfg);
++
++ break;
++ }
++
++ /* check if the tuner is there */
++ cmd.wlen = 0;
++ cmd.rlen = 1;
++ ret = si2157_cmd_execute(client, &cmd);
++ /* verify no i2c error and CTS is set */
++ if (ret && (ret != -EAGAIN)) {
++ pr_info("no HW found ret=%d\n", ret);
++ goto fail;
++ }
++
++ memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops));
++
++#ifdef CONFIG_MEDIA_CONTROLLER
++ if (instance == 1 && cfg->mdev) {
++ pr_info("cfg->mdev=%p\n", cfg->mdev);
++ dev->mdev = cfg->mdev;
++
++ dev->ent.name = KBUILD_MODNAME;
++ dev->ent.function = MEDIA_ENT_F_TUNER;
++
++ dev->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
++ dev->pad[TUNER_PAD_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
++ dev->pad[TUNER_PAD_AUD_OUT].flags = MEDIA_PAD_FL_SOURCE;
++
++ ret = media_entity_pads_init(&dev->ent, TUNER_NUM_PADS,
++ &dev->pad[0]);
++
++ if (ret)
++ goto fail;
++
++ ret = media_device_register_entity(cfg->mdev, &dev->ent);
++ if (ret) {
++ pr_info("media_device_regiser_entity returns %d\n", ret);
++ media_entity_cleanup(&dev->ent);
++ goto fail;
++ }
++ }
++#endif
++
++ mutex_unlock(&si2157_list_mutex);
++
++ return fe;
++
++fail:
++ pr_info("%s: Failed\n", __func__);
++ mutex_unlock(&si2157_list_mutex);
++
++ if (fe && (instance == 1))
++ si2157_release(fe);
++ return NULL;
++}
++EXPORT_SYMBOL(si2157_attach);
++
++MODULE_DESCRIPTION("Silicon Labs Si2141/2146/2147/2148/2157/2158 silicon tuner driver");
+ MODULE_AUTHOR("Antti Palosaari ");
+ MODULE_LICENSE("GPL");
+ MODULE_FIRMWARE(SI2158_A20_FIRMWARE);
+diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h
+index 76807f5..fd008cc 100644
+--- a/drivers/media/tuners/si2157.h
++++ b/drivers/media/tuners/si2157.h
+@@ -46,4 +46,18 @@ struct si2157_config {
+ u8 if_port;
+ };
+
++#if IS_REACHABLE(CONFIG_MEDIA_TUNER_SI2157)
++extern struct dvb_frontend *si2157_attach(struct dvb_frontend *fe, u8 addr,
++ struct i2c_adapter *i2c,
++ struct si2157_config *cfg);
++#else
++static inline struct dvb_frontend *si2157_attach(struct dvb_frontend *fe,
++ u8 addr,
++ struct i2c_adapter *i2c,
++ struct si2157_config *cfg)
++{
++ pr_err("%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif
+ #endif
+diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h
+index e6436f7..b51259b 100644
+--- a/drivers/media/tuners/si2157_priv.h
++++ b/drivers/media/tuners/si2157_priv.h
+@@ -19,17 +19,24 @@
+
+ #include
+ #include
++#include "tuner-i2c.h"
+ #include "si2157.h"
+
+ /* state struct */
+ struct si2157_dev {
++ struct list_head hybrid_tuner_instance_list;
++ struct tuner_i2c_props i2c_props;
++
+ struct mutex i2c_mutex;
+ struct dvb_frontend *fe;
+ bool active;
+ bool inversion;
+ u8 chiptype;
++ u8 addr;
+ u8 if_port;
+ u32 if_frequency;
++ u32 bandwidth;
++ u32 frequency;
+ struct delayed_work stat_work;
+
+ #if defined(CONFIG_MEDIA_CONTROLLER)
+diff --git a/drivers/media/tuners/tuner-types.c b/drivers/media/tuners/tuner-types.c
+index 98bc15a..e022ab8 100644
+--- a/drivers/media/tuners/tuner-types.c
++++ b/drivers/media/tuners/tuner-types.c
+@@ -1433,6 +1433,16 @@ static struct tuner_params tuner_sony_btf_pg463z_params[] = {
+ },
+ };
+
++/* ------------ TUNER_SILABS_SI2157 NTSC/PAL/Digital ------------ */
++
++static struct tuner_params tuner_silabs_si2157_params[] = {
++ {
++ .type = TUNER_PARAM_TYPE_DIGITAL,
++ .ranges = tuner_fm1236_mk3_ntsc_ranges,
++ .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges),
++ },
++};
++
+ /* --------------------------------------------------------------------- */
+
+ struct tunertype tuners[] = {
+@@ -1941,6 +1951,11 @@ struct tunertype tuners[] = {
+ .params = tuner_sony_btf_pg463z_params,
+ .count = ARRAY_SIZE(tuner_sony_btf_pg463z_params),
+ },
++ [TUNER_SILABS_SI2157] = {
++ .name = "Silicon Labs Si2157 terrestrial/cable multistandard",
++ .params = tuner_silabs_si2157_params,
++ .count = ARRAY_SIZE(tuner_silabs_si2157_params),
++ },
+ };
+ EXPORT_SYMBOL(tuners);
+
+diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
+index 99c8b1a..e85ec3c 100644
+--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
++++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
+@@ -922,6 +922,84 @@ struct cx231xx_board cx231xx_boards[] = {
+ .gpio = NULL,
+ } },
+ },
++ [CX231XX_BOARD_HAUPPAUGE_935C] = {
++ .name = "Hauppauge WinTV-HVR-935C",
++ .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_1_MUX_3,
++ .has_dvb = 1,
++ .demod_addr = 0x64, /* 0xc8 >> 1 */
++ .norm = V4L2_STD_PAL,
++
++ .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,
++ } },
++ },
++ [CX231XX_BOARD_HAUPPAUGE_975] = {
++ .name = "Hauppauge WinTV-HVR-975",
++ .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_1_MUX_3,
++ .has_dvb = 1,
++ .demod_addr = 0x64, /* 0xc8 >> 1 */
++ .norm = V4L2_STD_ALL,
++
++ .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);
+
+@@ -953,6 +1031,10 @@ struct usb_device_id cx231xx_id_table[] = {
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
+ {USB_DEVICE(0x2040, 0xb123),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_955Q},
++ {USB_DEVICE(0x2040, 0xb151),
++ .driver_info = CX231XX_BOARD_HAUPPAUGE_935C},
++ {USB_DEVICE(0x2040, 0xb150),
++ .driver_info = CX231XX_BOARD_HAUPPAUGE_975},
+ {USB_DEVICE(0x2040, 0xb130),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx},
+ {USB_DEVICE(0x2040, 0xb131),
+diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
+index fb56540..20af416 100644
+--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
++++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
+@@ -55,7 +55,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+ #define CX231XX_DVB_MAX_PACKETS 64
+
+ struct cx231xx_dvb {
+- struct dvb_frontend *frontend;
++ struct dvb_frontend *frontend[2];
+
+ /* feed count management */
+ struct mutex lock;
+@@ -386,17 +386,17 @@ static int attach_xc5000(u8 addr, struct cx231xx *dev)
+ cfg.i2c_adap = cx231xx_get_i2c_adap(dev, dev->board.tuner_i2c_master);
+ cfg.i2c_addr = addr;
+
+- if (!dev->dvb->frontend) {
++ if (!dev->dvb->frontend[0]) {
+ dev_err(dev->dev, "%s/2: dvb frontend not attached. Can't attach xc5000\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+- fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg);
++ fe = dvb_attach(xc5000_attach, dev->dvb->frontend[0], &cfg);
+ if (!fe) {
+ dev_err(dev->dev, "%s/2: xc5000 attach failed\n", dev->name);
+- dvb_frontend_detach(dev->dvb->frontend);
+- dev->dvb->frontend = NULL;
++ dvb_frontend_detach(dev->dvb->frontend[0]);
++ dev->dvb->frontend[0] = NULL;
+ return -EINVAL;
+ }
+
+@@ -408,9 +408,9 @@ static int attach_xc5000(u8 addr, struct cx231xx *dev)
+
+ int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
+ {
+- if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
++ if ((dev->dvb != NULL) && (dev->dvb->frontend[0] != NULL)) {
+
+- struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
++ struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops;
+
+ if (dops->set_analog_params != NULL) {
+ struct analog_parameters params;
+@@ -421,7 +421,7 @@ int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
+ /*params.audmode = ; */
+
+ /* Set the analog parameters to set the frequency */
+- dops->set_analog_params(dev->dvb->frontend, ¶ms);
++ dops->set_analog_params(dev->dvb->frontend[0], ¶ms);
+ }
+
+ }
+@@ -433,15 +433,15 @@ int cx231xx_reset_analog_tuner(struct cx231xx *dev)
+ {
+ int status = 0;
+
+- if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
++ if ((dev->dvb != NULL) && (dev->dvb->frontend[0] != NULL)) {
+
+- struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
++ struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops;
+
+ if (dops->init != NULL && !dev->xc_fw_load_done) {
+
+ dev_dbg(dev->dev,
+ "Reloading firmware for XC5000\n");
+- status = dops->init(dev->dvb->frontend);
++ status = dops->init(dev->dvb->frontend[0]);
+ if (status == 0) {
+ dev->xc_fw_load_done = 1;
+ dev_dbg(dev->dev,
+@@ -481,19 +481,32 @@ static int register_dvb(struct cx231xx_dvb *dvb,
+ dvb_register_media_controller(&dvb->adapter, dev->media_dev);
+
+ /* Ensure all frontends negotiate bus access */
+- dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
++ dvb->frontend[0]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
++ if (dvb->frontend[1])
++ dvb->frontend[1]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
+
+ dvb->adapter.priv = dev;
+
+ /* register frontend */
+- result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
++ result = dvb_register_frontend(&dvb->adapter, dvb->frontend[0]);
+ if (result < 0) {
+ dev_warn(dev->dev,
+ "%s: dvb_register_frontend failed (errno = %d)\n",
+ dev->name, result);
+- goto fail_frontend;
++ goto fail_frontend0;
+ }
+
++ if (dvb->frontend[1]) {
++ result = dvb_register_frontend(&dvb->adapter, dvb->frontend[1]);
++ if (result < 0) {
++ dev_warn(dev->dev,
++ "%s: 2nd dvb_register_frontend failed (errno = %d)\n",
++ dev->name, result);
++ goto fail_frontend1;
++ }
++ }
++
++
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+@@ -569,9 +582,14 @@ fail_fe_hw:
+ fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+ fail_dmx:
+- dvb_unregister_frontend(dvb->frontend);
+-fail_frontend:
+- dvb_frontend_detach(dvb->frontend);
++ if (dvb->frontend[1])
++ dvb_unregister_frontend(dvb->frontend[1]);
++ dvb_unregister_frontend(dvb->frontend[0]);
++fail_frontend1:
++ if (dvb->frontend[1])
++ dvb_frontend_detach(dvb->frontend[1]);
++fail_frontend0:
++ dvb_frontend_detach(dvb->frontend[0]);
+ dvb_unregister_adapter(&dvb->adapter);
+ fail_adapter:
+ return result;
+@@ -585,8 +603,12 @@ static void unregister_dvb(struct cx231xx_dvb *dvb)
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(&dvb->demux);
+- dvb_unregister_frontend(dvb->frontend);
+- dvb_frontend_detach(dvb->frontend);
++ if (dvb->frontend[1])
++ dvb_unregister_frontend(dvb->frontend[1]);
++ dvb_unregister_frontend(dvb->frontend[0]);
++ if (dvb->frontend[1])
++ dvb_frontend_detach(dvb->frontend[1]);
++ dvb_frontend_detach(dvb->frontend[0]);
+ dvb_unregister_adapter(&dvb->adapter);
+ /* remove I2C tuner */
+ client = dvb->i2c_client_tuner;
+@@ -635,11 +657,11 @@ static int dvb_init(struct cx231xx *dev)
+ case CX231XX_BOARD_CNXT_CARRAERA:
+ case CX231XX_BOARD_CNXT_RDE_250:
+
+- dev->dvb->frontend = dvb_attach(s5h1432_attach,
++ dev->dvb->frontend[0] = dvb_attach(s5h1432_attach,
+ &dvico_s5h1432_config,
+ demod_i2c);
+
+- if (dev->dvb->frontend == NULL) {
++ if (dev->dvb->frontend[0] == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach s5h1432 front end\n");
+ result = -EINVAL;
+@@ -647,9 +669,9 @@ static int dvb_init(struct cx231xx *dev)
+ }
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+- if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
++ if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0],
+ tuner_i2c,
+ &cnxt_rde250_tunerconfig)) {
+ result = -EINVAL;
+@@ -660,11 +682,11 @@ static int dvb_init(struct cx231xx *dev)
+ case CX231XX_BOARD_CNXT_SHELBY:
+ case CX231XX_BOARD_CNXT_RDU_250:
+
+- dev->dvb->frontend = dvb_attach(s5h1411_attach,
++ dev->dvb->frontend[0] = dvb_attach(s5h1411_attach,
+ &xc5000_s5h1411_config,
+ demod_i2c);
+
+- if (dev->dvb->frontend == NULL) {
++ if (dev->dvb->frontend[0] == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach s5h1411 front end\n");
+ result = -EINVAL;
+@@ -672,9 +694,9 @@ static int dvb_init(struct cx231xx *dev)
+ }
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+- if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
++ if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0],
+ tuner_i2c,
+ &cnxt_rdu250_tunerconfig)) {
+ result = -EINVAL;
+@@ -683,11 +705,11 @@ static int dvb_init(struct cx231xx *dev)
+ break;
+ case CX231XX_BOARD_CNXT_RDE_253S:
+
+- dev->dvb->frontend = dvb_attach(s5h1432_attach,
++ dev->dvb->frontend[0] = dvb_attach(s5h1432_attach,
+ &dvico_s5h1432_config,
+ demod_i2c);
+
+- if (dev->dvb->frontend == NULL) {
++ if (dev->dvb->frontend[0] == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach s5h1432 front end\n");
+ result = -EINVAL;
+@@ -695,9 +717,9 @@ static int dvb_init(struct cx231xx *dev)
+ }
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+- if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
++ if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0],
+ 0x60, tuner_i2c,
+ &cnxt_rde253s_tunerconfig)) {
+ result = -EINVAL;
+@@ -707,11 +729,11 @@ static int dvb_init(struct cx231xx *dev)
+ case CX231XX_BOARD_CNXT_RDU_253S:
+ case CX231XX_BOARD_KWORLD_UB445_USB_HYBRID:
+
+- dev->dvb->frontend = dvb_attach(s5h1411_attach,
++ dev->dvb->frontend[0] = dvb_attach(s5h1411_attach,
+ &tda18271_s5h1411_config,
+ demod_i2c);
+
+- if (dev->dvb->frontend == NULL) {
++ if (dev->dvb->frontend[0] == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach s5h1411 front end\n");
+ result = -EINVAL;
+@@ -719,9 +741,9 @@ static int dvb_init(struct cx231xx *dev)
+ }
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+- if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
++ if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0],
+ 0x60, tuner_i2c,
+ &cnxt_rde253s_tunerconfig)) {
+ result = -EINVAL;
+@@ -734,11 +756,11 @@ static int dvb_init(struct cx231xx *dev)
+ "%s: looking for tuner / demod on i2c bus: %d\n",
+ __func__, i2c_adapter_id(tuner_i2c));
+
+- dev->dvb->frontend = dvb_attach(lgdt3305_attach,
++ dev->dvb->frontend[0] = dvb_attach(lgdt3305_attach,
+ &hcw_lgdt3305_config,
+ demod_i2c);
+
+- if (dev->dvb->frontend == NULL) {
++ if (dev->dvb->frontend[0] == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach LG3305 front end\n");
+ result = -EINVAL;
+@@ -746,9 +768,9 @@ static int dvb_init(struct cx231xx *dev)
+ }
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+- dvb_attach(tda18271_attach, dev->dvb->frontend,
++ dvb_attach(tda18271_attach, dev->dvb->frontend[0],
+ 0x60, tuner_i2c,
+ &hcw_tda18271_config);
+ break;
+@@ -761,7 +783,7 @@ static int dvb_init(struct cx231xx *dev)
+
+ /* attach demod */
+ memset(&si2165_pdata, 0, sizeof(si2165_pdata));
+- si2165_pdata.fe = &dev->dvb->frontend;
++ si2165_pdata.fe = &dev->dvb->frontend[0];
+ si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL;
+ si2165_pdata.ref_freq_hz = 16000000;
+
+@@ -771,7 +793,7 @@ static int dvb_init(struct cx231xx *dev)
+ info.platform_data = &si2165_pdata;
+ request_module(info.type);
+ client = i2c_new_device(demod_i2c, &info);
+- if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) {
++ if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend[0] == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach SI2165 front end\n");
+ result = -EINVAL;
+@@ -786,12 +808,12 @@ static int dvb_init(struct cx231xx *dev)
+
+ dvb->i2c_client_demod = client;
+
+- dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
++ dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+- dvb_attach(tda18271_attach, dev->dvb->frontend,
++ dvb_attach(tda18271_attach, dev->dvb->frontend[0],
+ 0x60,
+ tuner_i2c,
+ &hcw_tda18271_config);
+@@ -808,7 +830,7 @@ static int dvb_init(struct cx231xx *dev)
+
+ /* attach demod */
+ memset(&si2165_pdata, 0, sizeof(si2165_pdata));
+- si2165_pdata.fe = &dev->dvb->frontend;
++ si2165_pdata.fe = &dev->dvb->frontend[0];
+ si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT;
+ si2165_pdata.ref_freq_hz = 24000000;
+
+@@ -818,7 +840,7 @@ static int dvb_init(struct cx231xx *dev)
+ info.platform_data = &si2165_pdata;
+ request_module(info.type);
+ client = i2c_new_device(demod_i2c, &info);
+- if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend == NULL) {
++ if (client == NULL || client->dev.driver == NULL || dev->dvb->frontend[0] == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach SI2165 front end\n");
+ result = -EINVAL;
+@@ -835,14 +857,14 @@ static int dvb_init(struct cx231xx *dev)
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+
+- dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
++ dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+- si2157_config.fe = dev->dvb->frontend;
++ si2157_config.fe = dev->dvb->frontend[0];
+ #ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+ #endif
+@@ -857,14 +879,14 @@ static int dvb_init(struct cx231xx *dev)
+ tuner_i2c,
+ &info);
+ if (client == NULL || client->dev.driver == NULL) {
+- dvb_frontend_detach(dev->dvb->frontend);
++ dvb_frontend_detach(dev->dvb->frontend[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+- dvb_frontend_detach(dev->dvb->frontend);
++ dvb_frontend_detach(dev->dvb->frontend[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+@@ -882,26 +904,26 @@ static int dvb_init(struct cx231xx *dev)
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+
+- dev->dvb->frontend = dvb_attach(lgdt3306a_attach,
++ dev->dvb->frontend[0] = dvb_attach(lgdt3306a_attach,
+ &hauppauge_955q_lgdt3306a_config,
+ demod_i2c
+ );
+
+- if (dev->dvb->frontend == NULL) {
++ if (dev->dvb->frontend[0] == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach LGDT3306A frontend.\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+
+- dev->dvb->frontend->ops.i2c_gate_ctrl = NULL;
++ dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+- si2157_config.fe = dev->dvb->frontend;
++ si2157_config.fe = dev->dvb->frontend[0];
+ #ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+ #endif
+@@ -916,14 +938,14 @@ static int dvb_init(struct cx231xx *dev)
+ tuner_i2c,
+ &info);
+ if (client == NULL || client->dev.driver == NULL) {
+- dvb_frontend_detach(dev->dvb->frontend);
++ dvb_frontend_detach(dev->dvb->frontend[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+- dvb_frontend_detach(dev->dvb->frontend);
++ dvb_frontend_detach(dev->dvb->frontend[0]);
+ result = -ENODEV;
+ goto out_free;
+ }
+@@ -933,6 +955,182 @@ static int dvb_init(struct cx231xx *dev)
+ dev->dvb->i2c_client_tuner = client;
+ break;
+ }
++ case CX231XX_BOARD_HAUPPAUGE_935C:
++ {
++ struct i2c_client *client;
++ struct i2c_board_info info;
++ struct si2157_config si2157_config = {};
++ struct si2168_config si2168_config = {};
++ struct i2c_adapter *adapter;
++
++ memset(&info, 0, sizeof(struct i2c_board_info));
++
++ /* attach demodulator chip */
++ memset(&si2168_config, 0, sizeof(si2168_config));
++ si2168_config.ts_mode = SI2168_TS_SERIAL;
++ si2168_config.fe = &dev->dvb->frontend[0];
++ si2168_config.i2c_adapter = &adapter;
++ si2168_config.ts_clock_inv = true;
++
++ strlcpy(info.type, "si2168", sizeof(info.type));
++ info.addr = dev->board.demod_addr;
++ info.platform_data = &si2168_config;
++
++ request_module(info.type);
++ client = i2c_new_device(demod_i2c, &info);
++
++ if (client == NULL || client->dev.driver == NULL) {
++ result = -ENODEV;
++ goto out_free;
++ }
++
++ if (!try_module_get(client->dev.driver->owner)) {
++ i2c_unregister_device(client);
++ result = -ENODEV;
++ goto out_free;
++ }
++
++ dvb->i2c_client_demod = client;
++ dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
++
++ /* define general-purpose callback pointer */
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
++
++ /* attach tuner */
++ memset(&si2157_config, 0, sizeof(si2157_config));
++ si2157_config.fe = dev->dvb->frontend[0];
++#ifdef CONFIG_MEDIA_CONTROLLER_DVB
++ si2157_config.mdev = dev->media_dev;
++#endif
++ si2157_config.if_port = 1;
++ 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) {
++ module_put(dvb->i2c_client_demod->dev.driver->owner);
++ i2c_unregister_device(dvb->i2c_client_demod);
++ result = -ENODEV;
++ goto out_free;
++ }
++
++ if (!try_module_get(client->dev.driver->owner)) {
++ i2c_unregister_device(client);
++ module_put(dvb->i2c_client_demod->dev.driver->owner);
++ i2c_unregister_device(dvb->i2c_client_demod);
++ result = -ENODEV;
++ goto out_free;
++ }
++
++ dev->cx231xx_reset_analog_tuner = NULL;
++ dev->dvb->i2c_client_tuner = client;
++ break;
++ }
++ case CX231XX_BOARD_HAUPPAUGE_975:
++ {
++ struct i2c_client *client;
++ struct i2c_board_info info;
++ struct si2157_config si2157_config = {};
++ struct si2168_config si2168_config = {};
++ struct i2c_adapter *adapter;
++
++ memset(&info, 0, sizeof(struct i2c_board_info));
++
++ /* attach demodulator chip */
++ dev->dvb->frontend[0] = dvb_attach(lgdt3306a_attach,
++ &hauppauge_955q_lgdt3306a_config,
++ demod_i2c
++ );
++
++ if (dev->dvb->frontend[0] == NULL) {
++ dev_err(dev->dev,
++ "Failed to attach LGDT3306A frontend.\n");
++ result = -EINVAL;
++ goto out_free;
++ }
++
++ /* attach demodulator chip */
++ memset(&si2168_config, 0, sizeof(si2168_config));
++ si2168_config.ts_mode = SI2168_TS_SERIAL; /* from *.inf file */
++ si2168_config.fe = &dev->dvb->frontend[1];
++ si2168_config.i2c_adapter = &adapter;
++ si2168_config.ts_clock_inv = true;
++
++ strlcpy(info.type, "si2168", sizeof(info.type));
++ info.addr = dev->board.demod_addr;
++ info.platform_data = &si2168_config;
++
++ request_module(info.type);
++ client = i2c_new_device(demod_i2c, &info);
++ if (client == NULL || client->dev.driver == NULL) {
++ dvb_frontend_detach(dev->dvb->frontend[0]);
++ result = -ENODEV;
++ goto out_free;
++ }
++
++ if (!try_module_get(client->dev.driver->owner)) {
++ dvb_frontend_detach(dev->dvb->frontend[0]);
++ i2c_unregister_device(client);
++ result = -ENODEV;
++ goto out_free;
++ }
++
++ dvb->i2c_client_demod = client;
++ dev->dvb->frontend[1]->id = 1;
++
++ /* TODO: INSPECT THIS! Required for multiple frontends? */
++ dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
++ dev->dvb->frontend[1]->ops.i2c_gate_ctrl = NULL;
++
++ /* define general-purpose callback pointer */
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
++ dvb->frontend[1]->callback = cx231xx_tuner_callback;
++
++ /* attach tuner */
++ memset(&si2157_config, 0, sizeof(si2157_config));
++ si2157_config.fe = dev->dvb->frontend[0];
++#ifdef CONFIG_MEDIA_CONTROLLER_DVB
++ si2157_config.mdev = dev->media_dev;
++#endif
++ si2157_config.if_port = 1;
++ 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) {
++ module_put(dvb->i2c_client_demod->dev.driver->owner);
++ i2c_unregister_device(dvb->i2c_client_demod);
++ dvb_frontend_detach(dev->dvb->frontend[0]);
++ result = -ENODEV;
++ goto out_free;
++ }
++
++ if (!try_module_get(client->dev.driver->owner)) {
++ i2c_unregister_device(client);
++ module_put(dvb->i2c_client_demod->dev.driver->owner);
++ i2c_unregister_device(dvb->i2c_client_demod);
++ dvb_frontend_detach(dev->dvb->frontend[0]);
++ result = -ENODEV;
++ goto out_free;
++ }
++
++ dev->dvb->frontend[1]->tuner_priv =
++ dev->dvb->frontend[0]->tuner_priv;
++
++ dvb_attach(si2157_attach, dev->dvb->frontend[1], info.addr,
++ client->adapter, &si2157_config);
++
++ 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:
+
+@@ -940,11 +1138,11 @@ static int dvb_init(struct cx231xx *dev)
+ "%s: looking for demod on i2c bus: %d\n",
+ __func__, i2c_adapter_id(tuner_i2c));
+
+- dev->dvb->frontend = dvb_attach(mb86a20s_attach,
++ dev->dvb->frontend[0] = dvb_attach(mb86a20s_attach,
+ &pv_mb86a20s_config,
+ demod_i2c);
+
+- if (dev->dvb->frontend == NULL) {
++ if (dev->dvb->frontend[0] == NULL) {
+ dev_err(dev->dev,
+ "Failed to attach mb86a20s demod\n");
+ result = -EINVAL;
+@@ -952,9 +1150,9 @@ static int dvb_init(struct cx231xx *dev)
+ }
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+- dvb_attach(tda18271_attach, dev->dvb->frontend,
++ dvb_attach(tda18271_attach, dev->dvb->frontend[0],
+ 0x60, tuner_i2c,
+ &pv_tda18271_config);
+ break;
+@@ -969,7 +1167,7 @@ static int dvb_init(struct cx231xx *dev)
+
+ /* attach demodulator chip */
+ si2168_config.ts_mode = SI2168_TS_SERIAL; /* from *.inf file */
+- si2168_config.fe = &dev->dvb->frontend;
++ si2168_config.fe = &dev->dvb->frontend[0];
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.ts_clock_inv = true;
+
+@@ -994,7 +1192,7 @@ static int dvb_init(struct cx231xx *dev)
+ dvb->i2c_client_demod = client;
+
+ /* attach tuner chip */
+- si2157_config.fe = dev->dvb->frontend;
++ si2157_config.fe = dev->dvb->frontend[0];
+ #ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+ #endif
+@@ -1037,7 +1235,7 @@ static int dvb_init(struct cx231xx *dev)
+ /* attach demodulator chip */
+ mn88473_config.i2c_wr_max = 16;
+ mn88473_config.xtal = 25000000;
+- mn88473_config.fe = &dev->dvb->frontend;
++ mn88473_config.fe = &dev->dvb->frontend[0];
+
+ strlcpy(info.type, "mn88473", sizeof(info.type));
+ info.addr = dev->board.demod_addr;
+@@ -1060,10 +1258,10 @@ static int dvb_init(struct cx231xx *dev)
+ dvb->i2c_client_demod = client;
+
+ /* define general-purpose callback pointer */
+- dvb->frontend->callback = cx231xx_tuner_callback;
++ dvb->frontend[0]->callback = cx231xx_tuner_callback;
+
+ /* attach tuner chip */
+- dvb_attach(r820t_attach, dev->dvb->frontend,
++ dvb_attach(r820t_attach, dev->dvb->frontend[0],
+ tuner_i2c,
+ &astrometa_t2hybrid_r820t_config);
+ break;
+@@ -1074,7 +1272,7 @@ static int dvb_init(struct cx231xx *dev)
+ dev->name);
+ break;
+ }
+- if (NULL == dvb->frontend) {
++ if (NULL == dvb->frontend[0]) {
+ dev_err(dev->dev,
+ "%s/2: frontend initialization failed\n", dev->name);
+ result = -EINVAL;
+diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
+index 65b039c..fa993f7 100644
+--- a/drivers/media/usb/cx231xx/cx231xx.h
++++ b/drivers/media/usb/cx231xx/cx231xx.h
+@@ -81,6 +81,8 @@
+ #define CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD 23
+ #define CX231XX_BOARD_ASTROMETA_T2HYBRID 24
+ #define CX231XX_BOARD_THE_IMAGING_SOURCE_DFG_USB2_PRO 25
++#define CX231XX_BOARD_HAUPPAUGE_935C 26
++#define CX231XX_BOARD_HAUPPAUGE_975 27
+
+ /* Limits minimum and default number of buffers */
+ #define CX231XX_MIN_BUF 4
+diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
+index 4c57fd7..82c7f8a 100644
+--- a/drivers/media/usb/em28xx/em28xx-cards.c
++++ b/drivers/media/usb/em28xx/em28xx-cards.c
+@@ -508,8 +508,10 @@ static struct em28xx_reg_seq plex_px_bcud[] = {
+ };
+
+ /*
+- * 2040:0265 Hauppauge WinTV-dualHD DVB
+- * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM
++ * 2040:0265 Hauppauge WinTV-dualHD DVB Isoc
++ * 2040:8265 Hauppauge WinTV-dualHD DVB Bulk
++ * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM Isoc
++ * 2040:826d Hauppauge WinTV-dualHD ATSC/QAM Bulk
+ * reg 0x80/0x84:
+ * GPIO_0: Yellow LED tuner 1, 0=on, 1=off
+ * GPIO_1: Green LED tuner 1, 0=on, 1=off
+@@ -2392,7 +2394,8 @@ struct em28xx_board em28xx_boards[] = {
+ .has_dvb = 1,
+ },
+ /*
+- * 2040:0265 Hauppauge WinTV-dualHD (DVB version).
++ * 2040:0265 Hauppauge WinTV-dualHD (DVB version) Isoc.
++ * 2040:8265 Hauppauge WinTV-dualHD (DVB version) Bulk.
+ * Empia EM28274, 2x Silicon Labs Si2168, 2x Silicon Labs Si2157
+ */
+ [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB] = {
+@@ -2403,11 +2406,13 @@ struct em28xx_board em28xx_boards[] = {
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = hauppauge_dualhd_dvb,
+ .has_dvb = 1,
++ .has_dual_ts = 1,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .leds = hauppauge_dualhd_leds,
+ },
+ /*
+- * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM).
++ * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) Isoc.
++ * 2040:826d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) Bulk.
+ * Empia EM28274, 2x LG LGDT3306A, 2x Silicon Labs Si2157
+ */
+ [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595] = {
+@@ -2418,6 +2423,7 @@ struct em28xx_board em28xx_boards[] = {
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = hauppauge_dualhd_dvb,
+ .has_dvb = 1,
++ .has_dual_ts = 1,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .leds = hauppauge_dualhd_leds,
+ },
+@@ -2548,8 +2554,12 @@ struct usb_device_id em28xx_id_table[] = {
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
+ { USB_DEVICE(0x2040, 0x0265),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
++ { USB_DEVICE(0x2040, 0x8265),
++ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
+ { USB_DEVICE(0x2040, 0x026d),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
++ { USB_DEVICE(0x2040, 0x826d),
++ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
+ { USB_DEVICE(0x0438, 0xb002),
+ .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
+ { USB_DEVICE(0x2001, 0xf112),
+@@ -2610,7 +2620,13 @@ struct usb_device_id em28xx_id_table[] = {
+ .driver_info = EM28178_BOARD_PCTV_461E },
+ { USB_DEVICE(0x2013, 0x025f),
+ .driver_info = EM28178_BOARD_PCTV_292E },
+- { USB_DEVICE(0x2040, 0x0264), /* Hauppauge WinTV-soloHD */
++ { USB_DEVICE(0x2013, 0x0264), /* Hauppauge WinTV-soloHD 292e SE */
++ .driver_info = EM28178_BOARD_PCTV_292E },
++ { USB_DEVICE(0x2040, 0x0264), /* Hauppauge WinTV-soloHD Isoc */
++ .driver_info = EM28178_BOARD_PCTV_292E },
++ { USB_DEVICE(0x2040, 0x8264), /* Hauppauge OEM Generic WinTV-soloHD Bulk */
++ .driver_info = EM28178_BOARD_PCTV_292E },
++ { USB_DEVICE(0x2040, 0x8268), /* Hauppauge Retail WinTV-soloHD Bulk */
+ .driver_info = EM28178_BOARD_PCTV_292E },
+ { USB_DEVICE(0x0413, 0x6f07),
+ .driver_info = EM2861_BOARD_LEADTEK_VC100 },
+@@ -3240,7 +3256,8 @@ static void em28xx_release_resources(struct em28xx *dev)
+ em28xx_i2c_unregister(dev, 1);
+ em28xx_i2c_unregister(dev, 0);
+
+- usb_put_dev(udev);
++ if (dev->ts == PRIMARY_TS)
++ usb_put_dev(udev);
+
+ /* Mark device as unused */
+ clear_bit(dev->devno, em28xx_devused);
+@@ -3433,6 +3450,35 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
+ return 0;
+ }
+
++int em28xx_duplicate_dev(struct em28xx *dev)
++{
++ int nr;
++ struct em28xx *sec_dev = kzalloc(sizeof(*sec_dev), GFP_KERNEL);
++
++ if (sec_dev == NULL) {
++ dev->dev_next = NULL;
++ return -ENOMEM;
++ }
++ memcpy(sec_dev, dev, sizeof(sizeof(*sec_dev)));
++ /* Check to see next free device and mark as used */
++ do {
++ nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS);
++ if (nr >= EM28XX_MAXBOARDS) {
++ /* No free device slots */
++ printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
++ EM28XX_MAXBOARDS);
++ kfree(sec_dev);
++ dev->dev_next = NULL;
++ return -ENOMEM;
++ }
++ } while (test_and_set_bit(nr, em28xx_devused));
++ sec_dev->devno = nr;
++ snprintf(sec_dev->name, 28, "em28xx #%d", nr);
++ sec_dev->dev_next = NULL;
++ dev->dev_next = sec_dev;
++ return 0;
++}
++
+ /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+ #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
+@@ -3552,6 +3598,17 @@ static int em28xx_usb_probe(struct usb_interface *interface,
+ }
+ }
+ break;
++ case 0x85:
++ if (usb_endpoint_xfer_isoc(e)) {
++ if (size > dev->dvb_max_pkt_size_isoc_ts2) {
++ dev->dvb_ep_isoc_ts2 = e->bEndpointAddress;
++ dev->dvb_max_pkt_size_isoc_ts2 = size;
++ dev->dvb_alt_isoc = i;
++ }
++ } else {
++ dev->dvb_ep_bulk_ts2 = e->bEndpointAddress;
++ }
++ break;
+ }
+ }
+ /* NOTE:
+@@ -3566,6 +3623,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
+ * 0x83 isoc* => audio
+ * 0x84 isoc => digital
+ * 0x84 bulk => analog or digital**
++ * 0x85 isoc => digital TS2
++ * 0x85 bulk => digital TS2
+ * (*: audio should always be isoc)
+ * (**: analog, if ep 0x82 is isoc, otherwise digital)
+ *
+@@ -3633,6 +3692,10 @@ static int em28xx_usb_probe(struct usb_interface *interface,
+ dev->has_video = has_video;
+ dev->ifnum = ifnum;
+
++ dev->ts = PRIMARY_TS;
++ snprintf(dev->name, 28, "em28xx");
++ dev->dev_next = NULL;
++
+ if (has_vendor_audio) {
+ dev_err(&interface->dev,
+ "Audio interface %i found (Vendor Class)\n", ifnum);
+@@ -3712,6 +3775,65 @@ static int em28xx_usb_probe(struct usb_interface *interface,
+ dev->dvb_xfer_bulk ? "bulk" : "isoc");
+ }
+
++ if (dev->board.has_dual_ts && em28xx_duplicate_dev(dev) == 0) {
++ dev->dev_next->ts = SECONDARY_TS;
++ dev->dev_next->alt = -1;
++ dev->dev_next->is_audio_only = has_vendor_audio &&
++ !(has_video || has_dvb);
++ dev->dev_next->has_video = false;
++ dev->dev_next->ifnum = ifnum;
++ dev->dev_next->model = id->driver_info;
++
++ mutex_init(&dev->dev_next->lock);
++ retval = em28xx_init_dev(dev->dev_next, udev, interface,
++ dev->dev_next->devno);
++ if (retval)
++ goto err_free;
++
++ dev->dev_next->board.ir_codes = NULL; /* No IR for 2nd tuner */
++ dev->dev_next->board.has_ir_i2c = 0; /* No IR for 2nd tuner */
++
++ if (usb_xfer_mode < 0) {
++ if (dev->dev_next->board.is_webcam)
++ try_bulk = 1;
++ else
++ try_bulk = 0;
++ } else {
++ try_bulk = usb_xfer_mode > 0;
++ }
++
++ /* Select USB transfer types to use */
++ if (has_dvb) {
++ if (!dev->dvb_ep_isoc_ts2 ||
++ (try_bulk && dev->dvb_ep_bulk_ts2))
++ dev->dev_next->dvb_xfer_bulk = 1;
++ dev_info(&dev->intf->dev, "dvb ts2 set to %s mode.\n",
++ dev->dev_next->dvb_xfer_bulk ? "bulk" : "isoc");
++ }
++
++ dev->dev_next->dvb_ep_isoc = dev->dvb_ep_isoc_ts2;
++ dev->dev_next->dvb_ep_bulk = dev->dvb_ep_bulk_ts2;
++ dev->dev_next->dvb_max_pkt_size_isoc = dev->dvb_max_pkt_size_isoc_ts2;
++ dev->dev_next->dvb_alt_isoc = dev->dvb_alt_isoc;
++
++ /* Configuare hardware to support TS2*/
++ if (dev->dvb_xfer_bulk) {
++ /* The ep4 and ep5 are configuared for BULK */
++ em28xx_write_reg(dev, 0x0b, 0x96);
++ mdelay(100);
++ em28xx_write_reg(dev, 0x0b, 0x80);
++ mdelay(100);
++ } else {
++ /* The ep4 and ep5 are configuared for ISO */
++ em28xx_write_reg(dev, 0x0b, 0x96);
++ mdelay(100);
++ em28xx_write_reg(dev, 0x0b, 0x82);
++ mdelay(100);
++ }
++
++ kref_init(&dev->dev_next->ref);
++ }
++
+ kref_init(&dev->ref);
+
+ request_modules(dev);
+@@ -3754,15 +3876,29 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
+ if (!dev)
+ return;
+
++ if (dev->dev_next != NULL) {
++ dev->dev_next->disconnected = 1;
++ dev_info(&dev->intf->dev, "Disconnecting %s\n",
++ dev->dev_next->name);
++ flush_request_modules(dev->dev_next);
++ }
++
+ dev->disconnected = 1;
+
+- dev_err(&dev->intf->dev, "Disconnecting\n");
++ dev_err(&dev->intf->dev, "Disconnecting %s\n", dev->name);
+
+ flush_request_modules(dev);
+
+ em28xx_close_extension(dev);
+
++ if (dev->dev_next != NULL)
++ em28xx_release_resources(dev->dev_next);
+ em28xx_release_resources(dev);
++
++ if (dev->dev_next != NULL) {
++ kref_put(&dev->dev_next->ref, em28xx_free_device);
++ dev->dev_next = NULL;
++ }
+ kref_put(&dev->ref, em28xx_free_device);
+ }
+
+diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
+index 1d0d8cc..b72335e 100644
+--- a/drivers/media/usb/em28xx/em28xx-core.c
++++ b/drivers/media/usb/em28xx/em28xx-core.c
+@@ -638,10 +638,30 @@ int em28xx_capture_start(struct em28xx *dev, int start)
+ dev->chip_id == CHIP_ID_EM28174 ||
+ dev->chip_id == CHIP_ID_EM28178) {
+ /* The Transport Stream Enable Register moved in em2874 */
+- rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+- start ?
+- EM2874_TS1_CAPTURE_ENABLE : 0x00,
+- EM2874_TS1_CAPTURE_ENABLE);
++ if (dev->dvb_xfer_bulk) {
++ /* Max Tx Size = 188 * EM28XX_DVB_BULK_PACKET_MULTIPLIER */
++ em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
++ EM2874_R5D_TS1_PKT_SIZE :
++ EM2874_R5E_TS2_PKT_SIZE,
++ EM28XX_DVB_BULK_PACKET_MULTIPLIER);
++ } else {
++ /* TS2 Maximum Transfer Size = 188 * 5 */
++ em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
++ EM2874_R5D_TS1_PKT_SIZE :
++ EM2874_R5E_TS2_PKT_SIZE, 0x05);
++ }
++ if (dev->ts == PRIMARY_TS)
++ rc = em28xx_write_reg_bits(dev,
++ EM2874_R5F_TS_ENABLE,
++ start ?
++ EM2874_TS1_CAPTURE_ENABLE : 0x00,
++ EM2874_TS1_CAPTURE_ENABLE);
++ else
++ rc = em28xx_write_reg_bits(dev,
++ EM2874_R5F_TS_ENABLE,
++ start ?
++ EM2874_TS2_CAPTURE_ENABLE : 0x00,
++ EM2874_TS2_CAPTURE_ENABLE);
+ } else {
+ /* FIXME: which is the best order? */
+ /* video registers are sampled by VREF */
+@@ -1077,7 +1097,11 @@ int em28xx_register_extension(struct em28xx_ops *ops)
+ mutex_lock(&em28xx_devlist_mutex);
+ list_add_tail(&ops->next, &em28xx_extension_devlist);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+- ops->init(dev);
++ if (ops->init) {
++ ops->init(dev);
++ if (dev->dev_next != NULL)
++ ops->init(dev->dev_next);
++ }
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+ pr_info("em28xx: Registered (%s) extension\n", ops->name);
+@@ -1091,7 +1115,11 @@ void em28xx_unregister_extension(struct em28xx_ops *ops)
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+- ops->fini(dev);
++ if (ops->fini) {
++ if (dev->dev_next != NULL)
++ ops->fini(dev->dev_next);
++ ops->fini(dev);
++ }
+ }
+ list_del(&ops->next);
+ mutex_unlock(&em28xx_devlist_mutex);
+@@ -1106,8 +1134,11 @@ void em28xx_init_extension(struct em28xx *dev)
+ mutex_lock(&em28xx_devlist_mutex);
+ list_add_tail(&dev->devlist, &em28xx_devlist);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+- if (ops->init)
++ if (ops->init) {
+ ops->init(dev);
++ if (dev->dev_next != NULL)
++ ops->init(dev->dev_next);
++ }
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+ }
+@@ -1118,8 +1149,11 @@ void em28xx_close_extension(struct em28xx *dev)
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+- if (ops->fini)
++ if (ops->fini) {
++ if (dev->dev_next != NULL)
++ ops->fini(dev->dev_next);
+ ops->fini(dev);
++ }
+ }
+ list_del(&dev->devlist);
+ mutex_unlock(&em28xx_devlist_mutex);
+@@ -1134,6 +1168,8 @@ int em28xx_suspend_extension(struct em28xx *dev)
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->suspend)
+ ops->suspend(dev);
++ if (dev->dev_next != NULL)
++ ops->suspend(dev->dev_next);
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+ return 0;
+@@ -1148,6 +1184,8 @@ int em28xx_resume_extension(struct em28xx *dev)
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->resume)
+ ops->resume(dev);
++ if (dev->dev_next != NULL)
++ ops->resume(dev->dev_next);
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+ return 0;
+diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
+index c4abf51..a7447e9 100644
+--- a/drivers/media/usb/em28xx/em28xx-dvb.c
++++ b/drivers/media/usb/em28xx/em28xx-dvb.c
+@@ -199,13 +199,12 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
+ int rc;
+ struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv;
+ struct em28xx *dev = i2c_bus->dev;
+- struct usb_device *udev = interface_to_usbdev(dev->intf);
+ int dvb_max_packet_size, packet_multiplier, dvb_alt;
+
+ if (dev->dvb_xfer_bulk) {
+ if (!dev->dvb_ep_bulk)
+ return -ENODEV;
+- dvb_max_packet_size = 512; /* USB 2.0 spec */
++ dvb_max_packet_size = 188;
+ packet_multiplier = EM28XX_DVB_BULK_PACKET_MULTIPLIER;
+ dvb_alt = 0;
+ } else { /* isoc */
+@@ -218,7 +217,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
+ dvb_alt = dev->dvb_alt_isoc;
+ }
+
+- usb_set_interface(udev, dev->ifnum, dvb_alt);
+ rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
+ if (rc < 0)
+ return rc;
+@@ -1128,8 +1126,9 @@ static void em28xx_unregister_dvb(struct em28xx_dvb *dvb)
+
+ static int em28xx_dvb_init(struct em28xx *dev)
+ {
+- int result = 0;
++ int result = 0, dvb_alt = 0;
+ struct em28xx_dvb *dvb;
++ struct usb_device *udev;
+
+ if (dev->is_audio_only) {
+ /* Shouldn't initialize IR for this interface */
+@@ -1155,7 +1154,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
+ result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
+ dev->dvb_xfer_bulk,
+ EM28XX_DVB_NUM_BUFS,
+- 512,
++ 188,
+ EM28XX_DVB_BULK_PACKET_MULTIPLIER);
+ } else {
+ result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
+@@ -1728,6 +1727,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &dvb->fe[0];
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
++ si2168_config.inversion = true;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+ info.addr = 0x64;
+@@ -1912,9 +1912,13 @@ static int em28xx_dvb_init(struct em28xx *dev)
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &dvb->fe[0];
+ si2168_config.ts_mode = SI2168_TS_SERIAL;
++ si2168_config.inversion = true;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+- info.addr = 0x64;
++ if (dev->ts == PRIMARY_TS)
++ info.addr = 0x64;
++ else
++ info.addr = 0x67;
+ info.platform_data = &si2168_config;
+ request_module(info.type);
+ client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
+@@ -1940,7 +1944,10 @@ static int em28xx_dvb_init(struct em28xx *dev)
+ #endif
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+- info.addr = 0x60;
++ if (dev->ts == PRIMARY_TS)
++ info.addr = 0x60;
++ else
++ info.addr = 0x63;
+ info.platform_data = &si2157_config;
+ request_module(info.type);
+ client = i2c_new_device(adapter, &info);
+@@ -1976,7 +1983,10 @@ static int em28xx_dvb_init(struct em28xx *dev)
+ lgdt3306a_config.fe = &dvb->fe[0];
+ lgdt3306a_config.i2c_adapter = &adapter;
+ strlcpy(info.type, "lgdt3306a", sizeof(info.type));
+- info.addr = 0x59;
++ if (dev->ts == PRIMARY_TS)
++ info.addr = 0x59;
++ else
++ info.addr = 0x0e;
+ info.platform_data = &lgdt3306a_config;
+ request_module(info.type);
+ client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus],
+@@ -2003,7 +2013,10 @@ static int em28xx_dvb_init(struct em28xx *dev)
+ #endif
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", sizeof(info.type));
+- info.addr = 0x60;
++ if (dev->ts == PRIMARY_TS)
++ info.addr = 0x60;
++ else
++ info.addr = 0x62;
+ info.platform_data = &si2157_config;
+ request_module(info.type);
+
+@@ -2046,6 +2059,14 @@ static int em28xx_dvb_init(struct em28xx *dev)
+ if (result < 0)
+ goto out_free;
+
++ if (dev->dvb_xfer_bulk) {
++ dvb_alt = 0;
++ } else { /* isoc */
++ dvb_alt = dev->dvb_alt_isoc;
++ }
++
++ udev = interface_to_usbdev(dev->intf);
++ usb_set_interface(udev, dev->ifnum, dvb_alt);
+ dev_info(&dev->intf->dev, "DVB extension successfully initialized\n");
+
+ kref_get(&dev->ref);
+diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
+index 88084f2..7dcf0cb 100644
+--- a/drivers/media/usb/em28xx/em28xx.h
++++ b/drivers/media/usb/em28xx/em28xx.h
+@@ -166,7 +166,7 @@
+ #define EM28XX_STOP_AUDIO 0
+
+ /* maximum number of em28xx boards */
+-#define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */
++#define EM28XX_MAXBOARDS DVB_MAX_ADAPTERS /* All adapters could be em28xx */
+
+ /* maximum number of frames that can be queued */
+ #define EM28XX_NUM_FRAMES 5
+@@ -191,7 +191,7 @@
+ USB 2.0 spec says bulk packet size is always 512 bytes
+ */
+ #define EM28XX_BULK_PACKET_MULTIPLIER 384
+-#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384
++#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 240
+
+ #define EM28XX_INTERLACED_DEFAULT 1
+
+@@ -217,6 +217,9 @@
+ /* max. number of button state polling addresses */
+ #define EM28XX_NUM_BUTTON_ADDRESSES_MAX 5
+
++#define PRIMARY_TS 0
++#define SECONDARY_TS 1
++
+ enum em28xx_mode {
+ EM28XX_SUSPEND,
+ EM28XX_ANALOG_MODE,
+@@ -457,6 +460,7 @@ struct em28xx_board {
+ unsigned int mts_firmware:1;
+ unsigned int max_range_640_480:1;
+ unsigned int has_dvb:1;
++ unsigned int has_dual_ts:1;
+ unsigned int is_webcam:1;
+ unsigned int valid:1;
+ unsigned int has_ir_i2c:1;
+@@ -621,6 +625,7 @@ struct em28xx {
+ unsigned int is_audio_only:1;
+ enum em28xx_int_audio_type int_audio_type;
+ enum em28xx_usb_audio_type usb_audio_type;
++ unsigned char name[32];
+
+ struct em28xx_board board;
+
+@@ -682,6 +687,8 @@ struct em28xx {
+ u8 ifnum; /* number of the assigned usb interface */
+ u8 analog_ep_isoc; /* address of isoc endpoint for analog */
+ u8 analog_ep_bulk; /* address of bulk endpoint for analog */
++ u8 dvb_ep_isoc_ts2; /* address of isoc endpoint for DVB TS2*/
++ u8 dvb_ep_bulk_ts2; /* address of bulk endpoint for DVB TS2*/
+ u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */
+ u8 dvb_ep_bulk; /* address of bulk endpoint for DVB */
+ int alt; /* alternate setting */
+@@ -695,6 +702,8 @@ struct em28xx {
+ int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */
+ unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the
+ selected DVB ep at dvb_alt */
++ unsigned int dvb_max_pkt_size_isoc_ts2; /* isoc max packet size of the
++ selected DVB ep at dvb_alt */
+ unsigned int dvb_xfer_bulk:1; /* use bulk instead of isoc
+ transfers for DVB */
+ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
+@@ -726,6 +735,9 @@ struct em28xx {
+ struct media_entity input_ent[MAX_EM28XX_INPUT];
+ struct media_pad input_pad[MAX_EM28XX_INPUT];
+ #endif
++
++ struct em28xx *dev_next;
++ int ts;
+ };
+
+ #define kref_to_dev(d) container_of(d, struct em28xx, ref)
+diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c
+index 82852f2..f5d442d 100644
+--- a/drivers/media/v4l2-core/tuner-core.c
++++ b/drivers/media/v4l2-core/tuner-core.c
+@@ -40,6 +40,7 @@
+ #include "xc5000.h"
+ #include "tda18271.h"
+ #include "xc4000.h"
++#include "si2157.h"
+
+ #define UNSET (-1U)
+
+@@ -396,6 +397,26 @@ static void set_type(struct i2c_client *c, unsigned int type,
+ tune_now = 0;
+ break;
+ }
++ case TUNER_SILABS_SI2157:
++ {
++ static struct si2157_config silabs_config = {
++ .inversion = true,
++ .if_port = 1, /* selects the digital IF port */
++ /* analog assumed to be other port */
++ };
++
++ dprintk("%s: looking for si2157 tuner on i2c bus: %d\n",
++ __func__, i2c_adapter_id(t->i2c->adapter));
++
++ if (!dvb_attach(si2157_attach, &t->fe, t->i2c->addr,
++ t->i2c->adapter, &silabs_config)) {
++ dprintk("%s: attaching si2157 tuner failed\n", __func__);
++ goto attach_failed;
++ }
++ dprintk("%s: si2157 tuner attached\n", __func__);
++ tune_now = 0;
++ break;
++ }
+ default:
+ if (!dvb_attach(simple_tuner_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr, t->type))
+diff --git a/include/media/tuner.h b/include/media/tuner.h
+index b3edc14..cab980a 100644
+--- a/include/media/tuner.h
++++ b/include/media/tuner.h
+@@ -142,6 +142,8 @@
+ #define TUNER_SONY_BTF_PK467Z 90 /* NTSC_JP */
+ #define TUNER_SONY_BTF_PB463Z 91 /* NTSC */
+
++#define TUNER_SILABS_SI2157 92 /* Silicon Labs terrestrial/cable tuner series */
++
+ /* tv card specific */
+ #define TDA9887_PRESENT (1<<0)
+ #define TDA9887_PORT1_INACTIVE (1<<1)
+--
+2.14.1
+
diff --git a/packages/linux-driver-addons/dvb/hauppauge/sources/backports/temp_revert.patch b/packages/linux-driver-addons/dvb/hauppauge/sources/backports/temp_revert.patch
new file mode 100644
index 0000000000..1ebe3ac3f6
--- /dev/null
+++ b/packages/linux-driver-addons/dvb/hauppauge/sources/backports/temp_revert.patch
@@ -0,0 +1,30 @@
+reverted: pvrusb2: properly check endpoint types
+
+--- b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
++++ a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+@@ -3648,12 +3648,6 @@
+ hdw);
+ hdw->ctl_write_urb->actual_length = 0;
+ hdw->ctl_write_pend_flag = !0;
+- if (usb_urb_ep_type_check(hdw->ctl_write_urb)) {
+- pvr2_trace(
+- PVR2_TRACE_ERROR_LEGS,
+- "Invalid write control endpoint");
+- return -EINVAL;
+- }
+ status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL);
+ if (status < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+@@ -3678,12 +3672,6 @@
+ hdw);
+ hdw->ctl_read_urb->actual_length = 0;
+ hdw->ctl_read_pend_flag = !0;
+- if (usb_urb_ep_type_check(hdw->ctl_read_urb)) {
+- pvr2_trace(
+- PVR2_TRACE_ERROR_LEGS,
+- "Invalid read control endpoint");
+- return -EINVAL;
+- }
+ status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL);
+ if (status < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,