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,