From 220a9e53eb1877cf9a29df1ed58cab7d4f06f384 Mon Sep 17 00:00:00 2001 From: cvh Date: Sun, 10 Dec 2017 18:50:29 +0100 Subject: [PATCH 01/11] media_build: remove package --- packages/linux-drivers/media_build/package.mk | 60 -- .../media_build-02-add-to-backports.patch | 17 - ...1056c5564eec8a1b169c6e84ff3.6.114c13.patch | 12 - ...tomer-code-restriction-in-rc6-decode.patch | 28 - .../linux-062-imon_pad_ignore_diagonal.patch | 21 - ...x-203-stb0899_enable_low_symbol_rate.patch | 11 - .../linux-204-lirc_fix_for_4.12.patch | 24 - ...auppauge_dualhd_second_tuner_support.patch | 586 ------------------ projects/Generic/options | 2 +- projects/RPi/options | 2 +- 10 files changed, 2 insertions(+), 761 deletions(-) delete mode 100644 packages/linux-drivers/media_build/package.mk delete mode 100644 packages/linux-drivers/media_build/patches/media_build-02-add-to-backports.patch delete mode 100644 packages/linux-drivers/media_build/sources/backports/linux-054-nuvoton_revert_d7b290a1056c5564eec8a1b169c6e84ff3.6.114c13.patch delete mode 100644 packages/linux-drivers/media_build/sources/backports/linux-057-Removed-MCE-customer-code-restriction-in-rc6-decode.patch delete mode 100644 packages/linux-drivers/media_build/sources/backports/linux-062-imon_pad_ignore_diagonal.patch delete mode 100644 packages/linux-drivers/media_build/sources/backports/linux-203-stb0899_enable_low_symbol_rate.patch delete mode 100644 packages/linux-drivers/media_build/sources/backports/linux-204-lirc_fix_for_4.12.patch delete mode 100644 packages/linux-drivers/media_build/sources/backports/linux-220-hauppauge_dualhd_second_tuner_support.patch diff --git a/packages/linux-drivers/media_build/package.mk b/packages/linux-drivers/media_build/package.mk deleted file mode 100644 index ed3ef6ba34..0000000000 --- a/packages/linux-drivers/media_build/package.mk +++ /dev/null @@ -1,60 +0,0 @@ -################################################################################ -# 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="media_build" -PKG_VERSION="2017-06-20-rpi" -PKG_SHA256="ff30bf1ee9fe342649ad80c9072ab4d37238d05680da850828f6d6c1d6b2e6d4" -PKG_ARCH="any" -PKG_LICENSE="GPL" -PKG_SITE="https://github.com/crazycat69/linux_media" -PKG_URL="$DISTRO_SRC/$PKG_NAME-$PKG_VERSION.tar.xz" -PKG_DEPENDS_TARGET="toolchain linux" -PKG_BUILD_DEPENDS_TARGET="toolchain linux" -PKG_NEED_UNPACK="$LINUX_DEPENDS" -PKG_SECTION="driver" -PKG_SHORTDESC="DVB drivers that replace the version shipped with the kernel" -PKG_LONGDESC="DVB drivers that replace the version shipped with the kernel" -PKG_IS_KERNEL_PKG="yes" - -pre_make_target() { - export KERNEL_VER=$(get_module_dir) - export LDFLAGS="" -} - -make_target() { - make untar - - # copy config file - if [ "$PROJECT" = Generic ] || [ "$PROJECT" = Virtual ]; then - if [ -f $PKG_DIR/config/generic.config ]; then - cp $PKG_DIR/config/generic.config v4l/.config - fi - else - if [ -f $PKG_DIR/config/usb.config ]; then - cp $PKG_DIR/config/usb.config v4l/.config - fi - fi - - # add menuconfig to edit .config - make VER=$KERNEL_VER SRCDIR=$(kernel_path) -} - -makeinstall_target() { - mkdir -p $INSTALL/$(get_full_module_dir)/updates - find $PKG_BUILD/v4l/ -name \*.ko -exec cp {} $INSTALL/$(get_full_module_dir)/updates \; -} diff --git a/packages/linux-drivers/media_build/patches/media_build-02-add-to-backports.patch b/packages/linux-drivers/media_build/patches/media_build-02-add-to-backports.patch deleted file mode 100644 index dfd0a88c2b..0000000000 --- a/packages/linux-drivers/media_build/patches/media_build-02-add-to-backports.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- a/backports/backports.txt -+++ b/backports/backports.txt -@@ -25,6 +25,14 @@ add api_version.patch - add pr_fmt.patch - add debug.patch - add drx39xxj.patch -+add linux-054-nuvoton_revert_d7b290a1056c5564eec8a1b169c6e84ff3.6.114c13.patch -+add linux-057-Removed-MCE-customer-code-restriction-in-rc6-decode.patch -+add linux-062-imon_pad_ignore_diagonal.patch -+add linux-202-lnbp22_patch_for_more_power_if_rotor.patch -+add linux-203-stb0899_enable_low_symbol_rate.patch -+add linux-204-lirc_fix_for_4.12.patch -+add linux-220-hauppauge_dualhd_second_tuner_support.patch -+add cxd2880-support.patch - - [4.10.255] - add v4.10_sched_signal.patch diff --git a/packages/linux-drivers/media_build/sources/backports/linux-054-nuvoton_revert_d7b290a1056c5564eec8a1b169c6e84ff3.6.114c13.patch b/packages/linux-drivers/media_build/sources/backports/linux-054-nuvoton_revert_d7b290a1056c5564eec8a1b169c6e84ff3.6.114c13.patch deleted file mode 100644 index 08566d1f13..0000000000 --- a/packages/linux-drivers/media_build/sources/backports/linux-054-nuvoton_revert_d7b290a1056c5564eec8a1b169c6e84ff3.6.114c13.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Naur linux-3.0/drivers/media/rc/nuvoton-cir.c linux-3.0.patch/drivers/media/rc/nuvoton-cir.c ---- linux-3.0/drivers/media/rc/nuvoton-cir.c 2011-07-22 04:17:23.000000000 +0200 -+++ linux-3.0.patch/drivers/media/rc/nuvoton-cir.c 2011-07-22 21:30:48.374591146 +0200 -@@ -1110,7 +1110,7 @@ - rdev->dev.parent = &pdev->dev; - rdev->driver_name = NVT_DRIVER_NAME; - rdev->map_name = RC_MAP_RC6_MCE; -- rdev->timeout = MS_TO_NS(100); -+ rdev->timeout = US_TO_NS(1000); - /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ - rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); - #if 0 diff --git a/packages/linux-drivers/media_build/sources/backports/linux-057-Removed-MCE-customer-code-restriction-in-rc6-decode.patch b/packages/linux-drivers/media_build/sources/backports/linux-057-Removed-MCE-customer-code-restriction-in-rc6-decode.patch deleted file mode 100644 index 6b09bc0073..0000000000 --- a/packages/linux-drivers/media_build/sources/backports/linux-057-Removed-MCE-customer-code-restriction-in-rc6-decode.patch +++ /dev/null @@ -1,28 +0,0 @@ ---- linux/drivers/media/rc/ir-rc6-decoder.c 2012-11-25 22:08:13.148418669 -0800 -+++ linux.patch/drivers/media/rc/ir-rc6-decoder.c 2012-11-25 22:07:48.864417975 -0800 -@@ -39,7 +39,6 @@ - #define RC6_STARTBIT_MASK 0x08 /* for the header bits */ - #define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ - #define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */ --#define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */ - #ifndef CHAR_BIT - #define CHAR_BIT 8 /* Normally in */ - #endif -@@ -257,14 +256,9 @@ again: - toggle = 0; - break; - case 32: -- if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { -- protocol = RC_TYPE_RC6_MCE; -- toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); -- scancode &= ~RC6_6A_MCE_TOGGLE_MASK; -- } else { -- protocol = RC_TYPE_RC6_6A_32; -- toggle = 0; -- } -+ protocol = RC_TYPE_RC6_MCE; -+ toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); -+ scancode &= ~RC6_6A_MCE_TOGGLE_MASK; - break; - default: - IR_dprintk(1, "RC6(6A) unsupported length\n"); diff --git a/packages/linux-drivers/media_build/sources/backports/linux-062-imon_pad_ignore_diagonal.patch b/packages/linux-drivers/media_build/sources/backports/linux-062-imon_pad_ignore_diagonal.patch deleted file mode 100644 index 677de3ed7f..0000000000 --- a/packages/linux-drivers/media_build/sources/backports/linux-062-imon_pad_ignore_diagonal.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -Naur linux-3.16.1/drivers/media/rc/imon.c linux-3.16.1.patch/drivers/media/rc/imon.c ---- linux-3.16.1/drivers/media/rc/imon.c 2014-08-14 04:36:35.000000000 +0200 -+++ linux-3.16.1.patch/drivers/media/rc/imon.c 2014-08-15 13:57:16.587620642 +0200 -@@ -1344,6 +1344,17 @@ - } - } else { - /* -+ * For users without stabilized, just ignore any value getting -+ * to close to the diagonal. -+ */ -+ if ((abs(rel_y) < 2 && abs(rel_x) < 2) || -+ abs(abs(rel_y) - abs(rel_x)) < 2 ) { -+ spin_lock_irqsave(&ictx->kc_lock, flags); -+ ictx->kc = KEY_UNKNOWN; -+ spin_unlock_irqrestore(&ictx->kc_lock, flags); -+ return; -+ } -+ /* - * Hack alert: instead of using keycodes, we have - * to use hard-coded scancodes here... - */ diff --git a/packages/linux-drivers/media_build/sources/backports/linux-203-stb0899_enable_low_symbol_rate.patch b/packages/linux-drivers/media_build/sources/backports/linux-203-stb0899_enable_low_symbol_rate.patch deleted file mode 100644 index 3624c9fa00..0000000000 --- a/packages/linux-drivers/media_build/sources/backports/linux-203-stb0899_enable_low_symbol_rate.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/drivers/media/dvb-frontends/stb0899_drv.c -+++ b/drivers/media/dvb-frontends/stb0899_drv.c -@@ -1618,7 +1618,7 @@ static const struct dvb_frontend_ops stb0899_ops = { - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, -- .symbol_rate_min = 5000000, -+ .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, - - .caps = FE_CAN_INVERSION_AUTO | diff --git a/packages/linux-drivers/media_build/sources/backports/linux-204-lirc_fix_for_4.12.patch b/packages/linux-drivers/media_build/sources/backports/linux-204-lirc_fix_for_4.12.patch deleted file mode 100644 index 7f5694afe0..0000000000 --- a/packages/linux-drivers/media_build/sources/backports/linux-204-lirc_fix_for_4.12.patch +++ /dev/null @@ -1,24 +0,0 @@ -Subject: [media] lirc: LIRC_GET_REC_RESOLUTION should return microseconds -Date: Tue, 11 Jul 2017 10:47:37 +0100 - -Since commit e8f4818895b3 ("[media] lirc: advertise -LIRC_CAN_GET_REC_RESOLUTION and improve") lircd uses the ioctl -LIRC_GET_REC_RESOLUTION to determine the shortest pulse or space that -the hardware can detect. This breaks decoding in lirc because lircd -expects the answer in microseconds, but nanoseconds is returned. - -drop at bump to 4.13 - -diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c -index a30af91..d2223c0 100644 ---- a/drivers/media/rc/ir-lirc-codec.c -+++ b/drivers/media/rc/ir-lirc-codec.c -@@ -266,7 +266,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, - if (!dev->rx_resolution) - return -ENOTTY; - -- val = dev->rx_resolution; -+ val = dev->rx_resolution / 1000; - break; - - case LIRC_SET_WIDEBAND_RECEIVER: diff --git a/packages/linux-drivers/media_build/sources/backports/linux-220-hauppauge_dualhd_second_tuner_support.patch b/packages/linux-drivers/media_build/sources/backports/linux-220-hauppauge_dualhd_second_tuner_support.patch deleted file mode 100644 index 46200b9b6c..0000000000 --- a/packages/linux-drivers/media_build/sources/backports/linux-220-hauppauge_dualhd_second_tuner_support.patch +++ /dev/null @@ -1,586 +0,0 @@ -From: Brad Love -Date: Sun, 16 Apr 2017 00:13:39 -0500 -Subject: Hauppauge DualHD DVB/ATSC second tuner support - -diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c -index c9b1eb3..c50769f 100644 ---- a/drivers/media/dvb-frontends/lgdt3306a.c -+++ b/drivers/media/dvb-frontends/lgdt3306a.c -@@ -624,6 +624,9 @@ static int lgdt3306a_set_modulation(struct lgdt3306a_state *state, - case QAM_256: - ret = lgdt3306a_set_qam(state, QAM_256); - break; -+ case QAM_AUTO: -+ ret = lgdt3306a_set_qam(state, QAM_64); -+ break; - default: - return -EINVAL; - } -@@ -649,6 +652,7 @@ static int lgdt3306a_agc_setup(struct lgdt3306a_state *state, - break; - case QAM_64: - case QAM_256: -+ case QAM_AUTO: - break; - default: - return -EINVAL; -@@ -703,6 +707,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; -@@ -726,6 +731,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: -@@ -1644,6 +1650,9 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe, - case QAM_256: - ref_snr = 2800; /* 28dB */ - break; -+ case QAM_AUTO: -+ ref_snr = 2200; /* 22dB */ -+ break; - default: - return -EINVAL; - } -diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c -index 146341a..d2e5b8d 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 -@@ -2376,7 +2378,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] = { -@@ -2387,11 +2390,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] = { -@@ -2402,6 +2407,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, - }, -@@ -2530,8 +2536,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), -@@ -3222,7 +3232,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); -@@ -3415,6 +3426,34 @@ 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)) - -@@ -3428,7 +3467,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, - struct usb_device *udev; - struct em28xx *dev = NULL; - int retval; -- bool has_vendor_audio = false, has_video = false, has_dvb = false; -+ bool has_vendor_audio = false, has_video = false; -+ bool has_dvb = false, has_dvb_ts2 = false; - int i, nr, try_bulk; - const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; - char *speed; -@@ -3534,6 +3574,19 @@ 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) { -+ has_dvb_ts2 = true; /* see NOTE (~) */ -+ dev->dvb_ep_isoc_ts2 = e->bEndpointAddress; -+ dev->dvb_max_pkt_size_isoc_ts2 = size; -+ dev->dvb_alt_isoc = i; -+ } -+ } else { -+ has_dvb_ts2 = true; -+ dev->dvb_ep_bulk_ts2 = e->bEndpointAddress; -+ } -+ break; - } - } - /* NOTE: -@@ -3615,6 +3668,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); -@@ -3694,6 +3751,61 @@ 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; -+ } -+ -+ 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); -@@ -3736,15 +3848,28 @@ 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..ca0ddd5 100644 ---- a/drivers/media/usb/em28xx/em28xx-core.c -+++ b/drivers/media/usb/em28xx/em28xx-core.c -@@ -638,10 +638,39 @@ 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) { -+ /* TS1 Maximum Transfer Size = 188 * EM28XX_DVB_BULK_PACKET_MULTIPLIER */ -+ em28xx_write_reg(dev, EM2874_R5D_TS1_PKT_SIZE, 0xef); -+ } else { -+ /* TS1 Maximum Transfer Size = 188 * 5 */ -+ em28xx_write_reg(dev, EM2874_R5D_TS1_PKT_SIZE, 0x05); -+ } -+ -+ if(dev->board.has_dual_ts) { -+ if(start) { -+ if(dev->dvb_xfer_bulk) { -+ /* TS2 Maximum Transfer Size = 188 * EM28XX_DVB_BULK_PACKET_MULTIPLIER */ -+ em28xx_write_reg(dev, EM2874_R5E_TS2_PKT_SIZE, 0xef); -+ } else { -+ /* TS2 Maximum Transfer Size = 188 * 5 */ -+ em28xx_write_reg(dev, EM2874_R5E_TS2_PKT_SIZE, 0x05); -+ } -+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, -+ (EM2874_TS1_CAPTURE_ENABLE | EM2874_TS2_CAPTURE_ENABLE), -+ (EM2874_TS1_CAPTURE_ENABLE | EM2874_TS2_CAPTURE_ENABLE)); -+ } else { -+ if(dev->ts == PRIMARY_TS) { -+ rc = em28xx_toggle_reg_bits(dev, EM2874_R5F_TS_ENABLE, EM2874_TS1_CAPTURE_ENABLE); -+ } else { -+ rc = em28xx_toggle_reg_bits(dev, EM2874_R5F_TS_ENABLE, EM2874_TS2_CAPTURE_ENABLE); -+ } -+ } -+ } else { -+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, -+ start ? -+ EM2874_TS1_CAPTURE_ENABLE : 0x00, -+ EM2874_TS1_CAPTURE_ENABLE); -+ } - } else { - /* FIXME: which is the best order? */ - /* video registers are sampled by VREF */ -@@ -1077,7 +1106,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 +1124,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 +1143,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 +1158,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 +1177,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 +1193,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 82edd37..e10b1ac 100644 ---- a/drivers/media/usb/em28xx/em28xx-dvb.c -+++ b/drivers/media/usb/em28xx/em28xx-dvb.c -@@ -199,13 +199,13 @@ 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); -+// 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 +218,8 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) - dvb_alt = dev->dvb_alt_isoc; - } - -- usb_set_interface(udev, dev->ifnum, dvb_alt); -+ /* moved to em28xx_dvb_init*/ -+ //usb_set_interface(udev, dev->ifnum, dvb_alt); - rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); - if (rc < 0) - return rc; -@@ -1128,8 +1129,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 +1157,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, -@@ -1913,7 +1915,8 @@ static int em28xx_dvb_init(struct em28xx *dev) - si2168_config.ts_mode = SI2168_TS_SERIAL; - 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); -@@ -1939,7 +1942,8 @@ 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); -@@ -1975,7 +1979,8 @@ 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], -@@ -2002,7 +2007,8 @@ 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); - -@@ -2045,6 +2051,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 e8d97d5..132638e 100644 ---- a/drivers/media/usb/em28xx/em28xx.h -+++ b/drivers/media/usb/em28xx/em28xx.h -@@ -190,7 +190,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 - -@@ -216,6 +216,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, -@@ -456,6 +459,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; -@@ -620,6 +624,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; - -@@ -681,6 +686,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 */ -@@ -694,6 +701,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 */ -@@ -725,6 +734,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) --- -2.7.4 - diff --git a/projects/Generic/options b/projects/Generic/options index ca9c5208a5..9364fdb4db 100644 --- a/projects/Generic/options +++ b/projects/Generic/options @@ -87,4 +87,4 @@ # for a list of additinoal drivers see packages/linux-drivers # Space separated list is supported, # e.g. ADDITIONAL_DRIVERS="DRIVER1 DRIVER2" - ADDITIONAL_DRIVERS="$ADDITIONAL_DRIVERS bcm_sta media_build intel_nuc_led" + ADDITIONAL_DRIVERS="$ADDITIONAL_DRIVERS bcm_sta intel_nuc_led" diff --git a/projects/RPi/options b/projects/RPi/options index a2ba616df3..d8dbb7c474 100644 --- a/projects/RPi/options +++ b/projects/RPi/options @@ -160,7 +160,7 @@ fi # for a list of additinoal drivers see packages/linux-drivers # Space separated list is supported, # e.g. ADDITIONAL_DRIVERS="DRIVER1 DRIVER2" - ADDITIONAL_DRIVERS="$ADDITIONAL_DRIVERS media_build rpi-cirrus-config" + ADDITIONAL_DRIVERS="$ADDITIONAL_DRIVERS rpi-cirrus-config" if [ "$DEVICE" = "Slice" -o "$DEVICE" = "Slice3" ]; then ADDITIONAL_DRIVERS="$ADDITIONAL_DRIVERS slice-drivers" From 48c14e212481dee6deb2b6fa64613ba30a55de1b Mon Sep 17 00:00:00 2001 From: cvh Date: Sun, 10 Dec 2017 18:50:31 +0100 Subject: [PATCH 02/11] dvb-addons: added mkpkg --- tools/mkpkg/mkpkg_pvr | 127 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100755 tools/mkpkg/mkpkg_pvr diff --git a/tools/mkpkg/mkpkg_pvr b/tools/mkpkg/mkpkg_pvr new file mode 100755 index 0000000000..82bbb54be3 --- /dev/null +++ b/tools/mkpkg/mkpkg_pvr @@ -0,0 +1,127 @@ +#!/bin/sh +################################################################################ +# 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 . +################################################################################ + +# set media_build version you want to use +if [ -z "$1" ]; then + echo "Usage: $0 crazycat | tbs" + exit 0 +else + MEDIA_BUILD_PROJECT="$1" +fi + +# name of the package +DVB_MKPKG_FOLDER="$(pwd)" + +# crazycat +if [ "$MEDIA_BUILD_PROJECT" = "crazycat" ]; then + MEDIA_BUILD_URL="https://bitbucket.org/CrazyCat/media_build.git" + MEDIA_TREE_URL="https://github.com/crazycat69/linux_media.git" + MEDIA_TREE_BRANCH="latest" + MEDIA_BUILD_NAME="crazycat" + +# tbs oss +elif [ "$MEDIA_BUILD_PROJECT" = "tbs" ]; then + MEDIA_BUILD_URL="https://github.com/tbsdtv/media_build.git" + MEDIA_TREE_URL="https://github.com/tbsdtv/linux_media.git" + MEDIA_TREE_BRANCH="latest" + MEDIA_BUILD_NAME="tbs" + +# exit +else + echo "exit: no valid project" + exit 0 +fi + +# remove old files +echo "removing old sources ..." +rm -rf $MEDIA_BUILD_NAME*/ + +################################################################################ + +# media_build dl + +echo "getting media_build sources ..." + +if [ ! -d media_build.git ]; then + git clone $MEDIA_BUILD_URL media_build/ +fi + +#get log +cd media_build/ + git pull + git checkout $MEDIA_BUILD_HASH + GIT_LOG_MEDIA_BUILD=`git log --pretty=oneline -n1` + +cd $DVB_MKPKG_FOLDER/ + +# media_tree dl +echo "getting sources ..." + if [ ! -d linux_media.git ]; then + git clone --depth=1 $MEDIA_TREE_URL -b $MEDIA_TREE_BRANCH media_tree + fi + +#get log +cd media_tree/ + git pull + GIT_LOG_MEDIA_TREE=`git log --pretty=oneline -n1` + GIT_REV=`git log -n1 --pretty=format:"%ad" --date=short` + +# hack/workaround for borked upstream kernel/media_build +# without removing atomisp there a lot additional includes that +# slowdown build process after modpost from 3min to 6min +# even if atomisp is disabled via kernel.conf + rm -rf drivers/staging/media/atomisp + sed -i 's|^.*drivers/staging/media/atomisp.*$||' $DVB_MKPKG_FOLDER/media_tree/drivers/staging/media/Kconfig + +cd $DVB_MKPKG_FOLDER/ + +################################################################################ + +# collecting files from media_tree +echo "create media_tree tar" +cd media_build/linux +make tar DIR="$DVB_MKPKG_FOLDER/media_tree/" + +cd $DVB_MKPKG_FOLDER + +# cleanup +rm -rf media_tree/ +rm -rf media_build/.git/ + +# rename buildfolder +mv media_build/ $MEDIA_BUILD_NAME-$GIT_REV/ + +# log used versions into LE_versions +echo "package include: $MEDIA_BUILD_NAME\n +media_build commit: $GIT_LOG_MEDIA_BUILD +media_build url: $MEDIA_BUILD_URL +\n +media_tree commit: $GIT_LOG_MEDIA_TREE +media_tree url: $MEDIA_TREE_URL +\n +package date: $(date +%F_%H:%M:%S)" > $MEDIA_BUILD_NAME-$GIT_REV/LE_versions + +################################################################################ + +# pack sources +echo "packing sources ..." +tar cvJf $MEDIA_BUILD_NAME-$GIT_REV.tar.xz $MEDIA_BUILD_NAME-$GIT_REV + +echo "remove temporary sourcedir ..." +rm -rf $MEDIA_BUILD_NAME-$GIT_REV From 5df12e5de5cb1d81764aaa2536799df1cf62b5a0 Mon Sep 17 00:00:00 2001 From: cvh Date: Sun, 10 Dec 2017 18:50:32 +0100 Subject: [PATCH 03/11] config/functions: add function install_driver_addon_files --- config/functions | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/config/functions b/config/functions index 91636f7847..e9f2976414 100644 --- a/config/functions +++ b/config/functions @@ -344,6 +344,25 @@ done -i "$addon_xml" } +install_driver_addon_files() { + if [ "$#" -eq 0 ] ; then + printf "$(print_color CLR_ERROR "no module search path defined")\n" + exit 1 + fi + + PKG_MODULE_DIR="$INSTALL/$(get_full_module_dir $PKG_ADDON_ID)/updates/$PKG_ADDON_ID" + PKG_ADDON_DIR="$INSTALL/usr/share/$MEDIACENTER/addons/$PKG_ADDON_ID" + + mkdir -p $PKG_MODULE_DIR + find $@ -name \*.ko -exec cp {} $PKG_MODULE_DIR \; + + find $PKG_MODULE_DIR -name \*.ko -exec $STRIP --strip-debug {} \; + + mkdir -p $PKG_ADDON_DIR + cp $PKG_DIR/changelog.txt $PKG_ADDON_DIR + install_addon_files "$PKG_ADDON_DIR" +} + install_addon_files() { install_addon_source "$1" install_addon_images "$1" From d9ae7fa43820e0f75a66afae485a48e855a30353 Mon Sep 17 00:00:00 2001 From: cvh Date: Sun, 10 Dec 2017 18:50:32 +0100 Subject: [PATCH 04/11] buildsystem: add DRIVER_ADDONS_SUPPORT --- config/functions | 6 ++++++ config/path | 2 +- packages/virtual/linux-drivers/package.mk | 6 +++++- projects/Generic/options | 8 ++++++++ projects/RPi/options | 8 ++++++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/config/functions b/config/functions index e9f2976414..3fc07d2ee1 100644 --- a/config/functions +++ b/config/functions @@ -653,6 +653,12 @@ show_config() { config_message="$config_message\n - Include driver:\t\t\t $config_driver" done + if [ "$DRIVER_ADDONS_SUPPORT" = "yes" ]; then + for config_driver_addons in $DRIVER_ADDONS; do + config_message="$config_message\n - Include driver add-ons:\t\t $config_driver_addons" + done + fi + for config_firmware in $FIRMWARE; do config_message="$config_message\n - Include firmware:\t\t\t $config_firmware" done diff --git a/config/path b/config/path index ed96f45eb9..8a0ddc822b 100644 --- a/config/path +++ b/config/path @@ -119,7 +119,7 @@ unset LD_LIBRARY_PATH if [ "$PKG_IS_ADDON" = "yes" ] ; then [ -z $PKG_SECTION ] && PKG_ADDON_ID="$PKG_NAME" || PKG_ADDON_ID="${PKG_SECTION//\//.}.$PKG_NAME" - PKG_NEED_UNPACK="${PKG_NEED_UNPACK} $(get_pkg_directory $MEDIACENTER)" + [ "$PKG_ADDON_IS_STANDALONE" != "yes" ] && PKG_NEED_UNPACK="${PKG_NEED_UNPACK} $(get_pkg_directory $MEDIACENTER)" fi # Automatically set PKG_SOURCE_NAME unless it is already defined. diff --git a/packages/virtual/linux-drivers/package.mk b/packages/virtual/linux-drivers/package.mk index d44273bb6d..05a5130134 100644 --- a/packages/virtual/linux-drivers/package.mk +++ b/packages/virtual/linux-drivers/package.mk @@ -20,9 +20,13 @@ PKG_NAME="linux-drivers" PKG_VERSION="" PKG_ARCH="any" PKG_LICENSE="GPL" -PKG_SITE="http://www.openelec.tv" +PKG_SITE="https://libreelec.tv" PKG_URL="" PKG_DEPENDS_TARGET="toolchain $ADDITIONAL_DRIVERS" PKG_SECTION="virtual" PKG_SHORTDESC="linux-drivers: Meta package to install additional drivers" PKG_LONGDESC="linux-drivers is a Meta package to install additional drivers" + +if [ "$DRIVER_ADDONS_SUPPORT" = "yes" ]; then + PKG_DEPENDS_TARGET="$PKG_DEPENDS_TARGET $DRIVER_ADDONS driverselect" +fi diff --git a/projects/Generic/options b/projects/Generic/options index 9364fdb4db..41e6871c9e 100644 --- a/projects/Generic/options +++ b/projects/Generic/options @@ -88,3 +88,11 @@ # Space separated list is supported, # e.g. ADDITIONAL_DRIVERS="DRIVER1 DRIVER2" ADDITIONAL_DRIVERS="$ADDITIONAL_DRIVERS bcm_sta intel_nuc_led" + + # build and install driver addons (yes / no) + DRIVER_ADDONS_SUPPORT="no" + + # driver addons to install: + # for a list of additinoal drivers see packages/linux-driver-addons + # Space separated list is supported, + DRIVER_ADDONS="" diff --git a/projects/RPi/options b/projects/RPi/options index d8dbb7c474..940d7cc660 100644 --- a/projects/RPi/options +++ b/projects/RPi/options @@ -165,3 +165,11 @@ fi if [ "$DEVICE" = "Slice" -o "$DEVICE" = "Slice3" ]; then ADDITIONAL_DRIVERS="$ADDITIONAL_DRIVERS slice-drivers" fi + + # build and install driver addons (yes / no) + DRIVER_ADDONS_SUPPORT="no" + + # driver addons to install: + # for a list of additinoal drivers see packages/linux-driver-addons + # Space separated list is supported, + DRIVER_ADDONS="" From 540822f224a8e35c4daa65347dd33fa4dd7a4d30 Mon Sep 17 00:00:00 2001 From: cvh Date: Sun, 10 Dec 2017 18:50:33 +0100 Subject: [PATCH 05/11] driver addons: add driver selector script --- .../addons/script/driverselect/package.mk | 50 +++++++++++++++++++ packages/mediacenter/kodi/package.mk | 5 ++ 2 files changed, 55 insertions(+) create mode 100644 packages/addons/script/driverselect/package.mk diff --git a/packages/addons/script/driverselect/package.mk b/packages/addons/script/driverselect/package.mk new file mode 100644 index 0000000000..601aaee471 --- /dev/null +++ b/packages/addons/script/driverselect/package.mk @@ -0,0 +1,50 @@ +################################################################################ +# 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 . +################################################################################ + +PKG_NAME="driverselect" +PKG_VERSION="2b79180" +PKG_SHA256="ed1e9defa185b46d4400794681f21f2bf1ddf8ccdfdb20e49ddde3edccdd6b7f" +PKG_REV="100" +PKG_ARCH="any" +PKG_LICENSE="OSS" +PKG_SITE="https://libreelec.tv" +PKG_URL="https://github.com/b-jesch/script.program.driverselect/archive/$PKG_VERSION.tar.gz" +PKG_SOURCE_DIR="${PKG_SECTION}.${PKG_NAME}-${PKG_VERSION}*" +PKG_DEPENDS_TARGET="toolchain" +PKG_SECTION="script.program" +PKG_SHORTDESC="script.program.driverselect" +PKG_LONGDESC="script.program.driverselect" +PKG_TOOLCHAIN="manual" + +PKG_IS_ADDON="yes" +PKG_ADDON_NAME="Driver Select" +PKG_ADDON_TYPE="xbmc.python.script" + +unpack() { + mkdir -p $PKG_BUILD/addon + tar --strip-components=1 -xf $SOURCES/$PKG_NAME/$PKG_NAME-$PKG_VERSION.tar.gz -C $PKG_BUILD/addon +} + +make_target() { + : +} + +makeinstall_target() { + mkdir -p $INSTALL/usr/share/kodi/addons/${PKG_SECTION}.${PKG_NAME} + cp -rP $PKG_BUILD/addon/* $INSTALL/usr/share/kodi/addons/${PKG_SECTION}.${PKG_NAME} +} diff --git a/packages/mediacenter/kodi/package.mk b/packages/mediacenter/kodi/package.mk index 4593d1f4f5..ff26714b0a 100644 --- a/packages/mediacenter/kodi/package.mk +++ b/packages/mediacenter/kodi/package.mk @@ -319,6 +319,11 @@ post_makeinstall_target() { xmlstarlet ed -L --subnode "/addons" -t elem -n "addon" -v "repository.libreelec.tv" $ADDON_MANIFEST xmlstarlet ed -L --subnode "/addons" -t elem -n "addon" -v "repository.retroplayer.libreelec.tv" $ADDON_MANIFEST xmlstarlet ed -L --subnode "/addons" -t elem -n "addon" -v "service.libreelec.settings" $ADDON_MANIFEST + + if [ "$DRIVER_ADDONS_SUPPORT" = "yes" ]; then + xmlstarlet ed -L --subnode "/addons" -t elem -n "addon" -v "script.program.driverselect" $ADDON_MANIFEST + fi + if [ "$DEVICE" = "Slice" -o "$DEVICE" = "Slice3" ]; then xmlstarlet ed -L --subnode "/addons" -t elem -n "addon" -v "service.slice" $ADDON_MANIFEST fi From b807b84ad02d013d98cda7acba31641276d6f03c Mon Sep 17 00:00:00 2001 From: cvh Date: Sun, 10 Dec 2017 18:50:34 +0100 Subject: [PATCH 06/11] media_tree: initial package --- .../dvb/depends/media_tree/package.mk | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 packages/linux-driver-addons/dvb/depends/media_tree/package.mk diff --git a/packages/linux-driver-addons/dvb/depends/media_tree/package.mk b/packages/linux-driver-addons/dvb/depends/media_tree/package.mk new file mode 100644 index 0000000000..19eababda8 --- /dev/null +++ b/packages/linux-driver-addons/dvb/depends/media_tree/package.mk @@ -0,0 +1,42 @@ +################################################################################ +# 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 . +################################################################################ + +PKG_NAME="media_tree" +PKG_VERSION="2017-11-14-f2ecc3d0787e" +PKG_SHA256="54aaa4cb2ab34804f42410dd461e587ecae36bd808c6e4accb3bcdae8c04579b" +PKG_ARCH="any" +PKG_LICENSE="GPL" +PKG_SITE="https://git.linuxtv.org/media_tree.git" +PKG_URL="http://linuxtv.org/downloads/drivers/linux-media-${PKG_VERSION}.tar.bz2" +PKG_DEPENDS_TARGET="toolchain" +PKG_SECTION="driver" +PKG_LONGDESC="Source of Linux Kernel media_tree subsystem to build with media_build." +PKG_TOOLCHAIN="manual" + +unpack() { + mkdir -p $PKG_BUILD/ + tar -xf $SOURCES/$PKG_NAME/$PKG_NAME-$PKG_VERSION.tar.bz2 -C $PKG_BUILD/ + + # hack/workaround for borked upstream kernel/media_build + # without removing atomisp there a lot additional includes that + # slowdown build process after modpost from 3min to 6min + # even if atomisp is disabled via kernel.conf + rm -rf $PKG_BUILD/drivers/staging/media/atomisp + sed -i 's|^.*drivers/staging/media/atomisp.*$||' \ + $PKG_BUILD/drivers/staging/media/Kconfig +} From 6d1d830638af5b4b25bfbcbb14de3d9f93e3bb82 Mon Sep 17 00:00:00 2001 From: cvh Date: Thu, 14 Dec 2017 12:12:59 +0100 Subject: [PATCH 07/11] driver.dvb: initial hauppauge package --- .../dvb/depends/media_tree/package.mk | 4 +- .../dvb/hauppauge/changelog.txt | 2 + .../dvb/hauppauge/icon/icon.png | Bin 0 -> 23076 bytes .../dvb/hauppauge/package.mk | 51 + ...ver.dvb.hauppauge-01-remove-rmmod.pl.patch | 11 + ...er.dvb.hauppauge-02-add-to-backports.patch | 11 + .../dvb/hauppauge/source/default.py | 17 + .../sources/backports/hauppauge.patch | 3214 +++++++++++++++++ .../sources/backports/temp_revert.patch | 30 + 9 files changed, 3338 insertions(+), 2 deletions(-) create mode 100755 packages/linux-driver-addons/dvb/hauppauge/changelog.txt create mode 100644 packages/linux-driver-addons/dvb/hauppauge/icon/icon.png create mode 100644 packages/linux-driver-addons/dvb/hauppauge/package.mk create mode 100644 packages/linux-driver-addons/dvb/hauppauge/patches/driver.dvb.hauppauge-01-remove-rmmod.pl.patch create mode 100644 packages/linux-driver-addons/dvb/hauppauge/patches/driver.dvb.hauppauge-02-add-to-backports.patch create mode 100644 packages/linux-driver-addons/dvb/hauppauge/source/default.py create mode 100644 packages/linux-driver-addons/dvb/hauppauge/sources/backports/hauppauge.patch create mode 100644 packages/linux-driver-addons/dvb/hauppauge/sources/backports/temp_revert.patch 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 0000000000000000000000000000000000000000..962157e96e76474e14170551b8bef0ca8607de2f GIT binary patch literal 23076 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%0DSdYn5%nglyP2bEUWPY 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 literal 0 HcmV?d00001 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, From bc4d9bb9b5407593052a4656ae5afa3722832762 Mon Sep 17 00:00:00 2001 From: cvh Date: Thu, 14 Dec 2017 12:12:54 +0100 Subject: [PATCH 08/11] driver.dvb: initial digitaldevices package --- .../dvb/digital_devices/changelog.txt | 2 + .../dvb/digital_devices/icon/icon.png | Bin 0 -> 32470 bytes .../dvb/digital_devices/package.mk | 45 ++++++++++++++++++ .../dvb/digital_devices/source/default.py | 17 +++++++ 4 files changed, 64 insertions(+) create mode 100755 packages/linux-driver-addons/dvb/digital_devices/changelog.txt create mode 100644 packages/linux-driver-addons/dvb/digital_devices/icon/icon.png create mode 100644 packages/linux-driver-addons/dvb/digital_devices/package.mk create mode 100644 packages/linux-driver-addons/dvb/digital_devices/source/default.py diff --git a/packages/linux-driver-addons/dvb/digital_devices/changelog.txt b/packages/linux-driver-addons/dvb/digital_devices/changelog.txt new file mode 100755 index 0000000000..32d81ca428 --- /dev/null +++ b/packages/linux-driver-addons/dvb/digital_devices/changelog.txt @@ -0,0 +1,2 @@ +100 +- Initial add-on diff --git a/packages/linux-driver-addons/dvb/digital_devices/icon/icon.png b/packages/linux-driver-addons/dvb/digital_devices/icon/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..10c6edc2676b3b90863e49d8ed66f2ecdbf896a2 GIT binary patch literal 32470 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. +################################################################################ + +PKG_NAME="digital_devices" +PKG_VERSION="0f05b19" +PKG_SHA256="0f12fa00133eaeb83c4e380cd6e3174d7b399e7e9e5ba8eac0c1ada7579c2c20" +PKG_ARCH="x86_64" +PKG_LICENSE="GPL" +PKG_SITE="https://github.com/DigitalDevices/dddvb/" +PKG_URL="https://github.com/DigitalDevices/dddvb/archive/${PKG_VERSION}.tar.gz" +PKG_SOURCE_DIR="dddvb-${PKG_VERSION}*" +PKG_DEPENDS_TARGET="toolchain linux" +PKG_BUILD_DEPENDS_TARGET="toolchain linux" +PKG_NEED_UNPACK="$LINUX_DEPENDS" +PKG_SECTION="driver.dvb" +PKG_LONGDESC="DVB driver for Digital Devices cards" + +PKG_IS_ADDON="yes" +PKG_ADDON_IS_STANDALONE="yes" +PKG_ADDON_NAME="DVB drivers for DigitalDevices" +PKG_ADDON_TYPE="xbmc.service" +PKG_ADDON_VERSION="${ADDON_VERSION}.${PKG_REV}" + +make_target() { + KDIR=$(kernel_path) make +} + +makeinstall_target() { + install_driver_addon_files "$PKG_BUILD/ddbridge $PKG_BUILD/dvb-core $PKG_BUILD/frontends" +} diff --git a/packages/linux-driver-addons/dvb/digital_devices/source/default.py b/packages/linux-driver-addons/dvb/digital_devices/source/default.py new file mode 100644 index 0000000000..fe3ba645a6 --- /dev/null +++ b/packages/linux-driver-addons/dvb/digital_devices/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 . +################################################################################ From 87e1f557b5cf243c17908f0b94eeb12d89e2a00e Mon Sep 17 00:00:00 2001 From: cvh Date: Thu, 14 Dec 2017 12:12:49 +0100 Subject: [PATCH 09/11] driver.dvb: initial crazycat package --- .../dvb/crazycat/changelog.txt | 2 + .../dvb/crazycat}/config/generic.config | 364 +++-- .../dvb/crazycat}/config/usb.config | 352 ++--- .../dvb/crazycat/icon/icon.png | Bin 0 -> 68596 bytes .../dvb/crazycat/package.mk | 63 + ...ver.dvb.crazycat-01-remove-rmmod.pl.patch} | 0 ...ver.dvb.crazycat-02-add-to-backports.patch | 11 + .../dvb/crazycat/source/default.py | 17 + ...lnbp22_patch_for_more_power_if_rotor.patch | 0 ...ux-220-Xbox-One-DVB-T2-stick-support.patch | 1386 +++++++++++++++++ 10 files changed, 1828 insertions(+), 367 deletions(-) create mode 100755 packages/linux-driver-addons/dvb/crazycat/changelog.txt rename packages/{linux-drivers/media_build => linux-driver-addons/dvb/crazycat}/config/generic.config (94%) rename packages/{linux-drivers/media_build => linux-driver-addons/dvb/crazycat}/config/usb.config (94%) create mode 100644 packages/linux-driver-addons/dvb/crazycat/icon/icon.png create mode 100644 packages/linux-driver-addons/dvb/crazycat/package.mk rename packages/{linux-drivers/media_build/patches/media_build-01-remove-rmmod.pl.patch => linux-driver-addons/dvb/crazycat/patches/driver.dvb.crazycat-01-remove-rmmod.pl.patch} (100%) create mode 100644 packages/linux-driver-addons/dvb/crazycat/patches/driver.dvb.crazycat-02-add-to-backports.patch create mode 100644 packages/linux-driver-addons/dvb/crazycat/source/default.py rename packages/{linux-drivers/media_build => linux-driver-addons/dvb/crazycat}/sources/backports/linux-202-lnbp22_patch_for_more_power_if_rotor.patch (100%) create mode 100644 packages/linux-driver-addons/dvb/crazycat/sources/backports/linux-220-Xbox-One-DVB-T2-stick-support.patch diff --git a/packages/linux-driver-addons/dvb/crazycat/changelog.txt b/packages/linux-driver-addons/dvb/crazycat/changelog.txt new file mode 100755 index 0000000000..32d81ca428 --- /dev/null +++ b/packages/linux-driver-addons/dvb/crazycat/changelog.txt @@ -0,0 +1,2 @@ +100 +- Initial add-on diff --git a/packages/linux-drivers/media_build/config/generic.config b/packages/linux-driver-addons/dvb/crazycat/config/generic.config similarity index 94% rename from packages/linux-drivers/media_build/config/generic.config rename to packages/linux-driver-addons/dvb/crazycat/config/generic.config index 36fb96b292..cb8f5ae5ca 100644 --- a/packages/linux-drivers/media_build/config/generic.config +++ b/packages/linux-driver-addons/dvb/crazycat/config/generic.config @@ -2,150 +2,191 @@ # Automatically generated file; DO NOT EDIT. # V4L/DVB menu # -# CONFIG_SOC_EXYNOS4212 is not set -# CONFIG_PWM_OMAP_DMTIMER is not set -# CONFIG_ARCH_OMAP3 is not set -# CONFIG_BROKEN is not set -# CONFIG_ARM_DMA_USE_IOMMU is not set -CONFIG_HDMI=y -CONFIG_LEDS_CLASS_FLASH=y -CONFIG_PCI_MSI=y -# CONFIG_MTK_IOMMU_V1 is not set -# CONFIG_RPMSG is not set -# CONFIG_PLAT_S3C24XX is not set -# CONFIG_FB_VIA is not set -# CONFIG_SND_FM801 is not set -# CONFIG_I2C_GPIO is not set -CONFIG_I2C=y +# CONFIG_ARCH_STI is not set +# CONFIG_TI_ST is not set CONFIG_HAVE_CLK=y -# CONFIG_REGULATOR is not set -CONFIG_PM=y -# CONFIG_ARCH_MEDIATEK is not set -# CONFIG_ISA is not set -CONFIG_DEBUG_FS=y -CONFIG_MODULES=y -# CONFIG_SPI is not set +CONFIG_I2C_MUX=m +# CONFIG_PLAT_M32700UT is not set +# CONFIG_METAG is not set +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_MXC is not set # CONFIG_MFD_WL1273_CORE is not set -CONFIG_SND_PCM=y -CONFIG_MMU=y -CONFIG_MFD_CORE=y -CONFIG_MMC=y -CONFIG_USB=y -CONFIG_SYSFS=y +CONFIG_VIRT_TO_BUS=y +CONFIG_LEDS_CLASS=y # CONFIG_MTD_COMPLEX_MAPPINGS is not set -CONFIG_FONT_SUPPORT=y -CONFIG_RATIONAL=y +CONFIG_TTY=y +CONFIG_ISA_DMA_API=y +CONFIG_USB_ACM=m +# CONFIG_SPARC32 is not set +# CONFIG_ARCH_MMP is not set +CONFIG_PCI_MSI=y +# CONFIG_MFD_TIMBERDALE is not set +CONFIG_FB=y +CONFIG_HAS_DMA=y +# CONFIG_SOC_DRA7XX is not set +# CONFIG_IMX_IPUV3_CORE is not set +CONFIG_NET=y +# CONFIG_PXA27x is not set +# CONFIG_ARCH_DAVINCI_DM365 is not set +# CONFIG_OF is not set +# CONFIG_SRAM is not set +# CONFIG_SND_SOC_SI476X is not set +# CONFIG_SONY_LAPTOP is not set +# CONFIG_QCOM_MDT_LOADER is not set +# CONFIG_ARCH_RENESAS is not set +# CONFIG_SPARC64 is not set +# CONFIG_ARCH_S3C64XX is not set +CONFIG_FONT_8x16=y +# CONFIG_FB_OMAP2 is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_PREEMPT is not set +# CONFIG_ARCH_S5PV210 is not set CONFIG_CRC32=y +CONFIG_SND=y +CONFIG_RFKILL=m +# CONFIG_M32R is not set +CONFIG_PINCTRL=y +# CONFIG_SG_SPLIT is not set +CONFIG_MODULES=y +CONFIG_REGMAP_I2C=y +# CONFIG_GENERIC_PHY is not set +# CONFIG_ARCH_ZX is not set +# CONFIG_MFD_SI476X_CORE is not set +CONFIG_DEBUG_FS=y +# CONFIG_FB_VIA is not set +CONFIG_REGMAP=y +CONFIG_PCI=y +# CONFIG_ARCH_MULTIPLATFORM is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_QCOM_SCM is not set +# CONFIG_PLAT_S5P is not set CONFIG_SERIO=y # CONFIG_ARCH_SUNXI is not set -CONFIG_USB_ARCH_HAS_HCD=y -# CONFIG_PLAT_S5P is not set -# CONFIG_OMAP_DM_TIMER is not set -# CONFIG_MFD_TIMBERDALE is not set -# CONFIG_SND_ISA is not set -CONFIG_ISA_DMA_API=y -CONFIG_NEW_LEDS=y -CONFIG_PCI=y -# CONFIG_ARCH_OMAP2PLUS is not set -# CONFIG_VIDEO_V4L1 is not set -CONFIG_FW_LOADER=y -CONFIG_DMA_ENGINE=y -# CONFIG_GENERIC_PHY is not set -CONFIG_HAS_IOMEM=y -CONFIG_NET=y -# CONFIG_ARCH_EXYNOS is not set -# CONFIG_SOC_AM43XX is not set -CONFIG_INET=y -# CONFIG_SPARC32 is not set -# CONFIG_TI_ST is not set -# CONFIG_STA2X11 is not set -# CONFIG_MTD is not set -CONFIG_USB_ACM=m -# CONFIG_ARCH_STM32 is not set -CONFIG_SND=y -CONFIG_PINCTRL=y -CONFIG_PNP=y -# CONFIG_SRAM is not set -# CONFIG_SG_SPLIT is not set -# CONFIG_SONY_LAPTOP is not set -# CONFIG_ARCH_S5PV210 is not set -CONFIG_COMMON_CLK=y -# CONFIG_SOC_IMX27 is not set -CONFIG_INPUT=y -# CONFIG_OF is not set -CONFIG_FONT_8x16=y -# CONFIG_SOC_EXYNOS5250 is not set -CONFIG_ACPI=y -# CONFIG_ARCH_MMP is not set -CONFIG_FIREWIRE=m -# CONFIG_PLAT_M32700UT is not set -CONFIG_DMA_SHARED_BUFFER=y -# CONFIG_PPC_MPC512x is not set -CONFIG_X86=y -# CONFIG_SND_MIRO is not set -# CONFIG_OMAP_IOMMU is not set -# CONFIG_ARCH_MULTIPLATFORM is not set -CONFIG_VIRT_TO_BUS=y -# CONFIG_ARCH_S3C64XX is not set -# CONFIG_ARCH_SHMOBILE is not set -# CONFIG_PXA27x is not set -# CONFIG_MFD_SI476X_CORE is not set -# CONFIG_ARM64 is not set -CONFIG_GPIOLIB=y -# CONFIG_FB_OMAP2 is not set # CONFIG_GENERIC_ALLOCATOR is not set -# CONFIG_OMAP2_VRFB is not set -CONFIG_BITREVERSE=y -# CONFIG_BLACKFIN is not set -# CONFIG_REGMAP_MMIO is not set -CONFIG_RFKILL=m -CONFIG_FB_CFB_FILLRECT=y -CONFIG_TTY=y -# CONFIG_SPI_MASTER is not set -CONFIG_LEDS_CLASS=y -# CONFIG_MTK_IOMMU is not set -CONFIG_FB_CFB_IMAGEBLIT=y -# CONFIG_METAG is not set -CONFIG_REGMAP=y -# CONFIG_ARCH_MESON is not set -# CONFIG_M32R is not set -CONFIG_SND_SOC=y -# CONFIG_ARCH_OMAP4 is not set -CONFIG_HAS_DMA=y -# CONFIG_MFD_SYSCON is not set -CONFIG_INPUT_EVDEV=y -# CONFIG_ARCH_AT91 is not set -# CONFIG_SPARC64 is not set -# CONFIG_ARCH_RENESAS is not set +# CONFIG_ISA is not set +# CONFIG_PWM is not set +CONFIG_SYSFS=y +# CONFIG_BROKEN is not set +CONFIG_MMC=y +# CONFIG_MTK_IOMMU_V1 is not set +# CONFIG_ARCH_TANGO is not set +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_GPIOLIB=y +# CONFIG_ARM64 is not set +# CONFIG_ARM_DMA_USE_IOMMU is not set +# CONFIG_PPC_MPC512x is not set +# CONFIG_OMAP_DM_TIMER is not set +# CONFIG_ARCH_EXYNOS is not set # CONFIG_ALPHA is not set +CONFIG_LEDS_CLASS_FLASH=y CONFIG_FB_CFB_COPYAREA=y -# CONFIG_ARCH_DAVINCI is not set -# CONFIG_ARCH_MXC is not set -CONFIG_FB=y -# CONFIG_ARCH_DAVINCI_DM365 is not set -# CONFIG_SOC_EXYNOS4412 is not set -# CONFIG_ARCH_STI is not set -CONFIG_I2C_MUX=m -# CONFIG_SND_SOC_SI476X is not set -CONFIG_REGMAP_I2C=y +CONFIG_INPUT_EVDEV=y +CONFIG_FIREWIRE=m +CONFIG_MFD_CORE=y +CONFIG_PM=y +CONFIG_DMA_ENGINE=y +CONFIG_SND_PCM=y +# CONFIG_PLAT_S3C24XX is not set +# CONFIG_SND_MIRO is not set +# CONFIG_SPI is not set +CONFIG_FW_LOADER=y +# CONFIG_MFD_SYSCON is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_IOMMU_DMA is not set +CONFIG_USB=y +CONFIG_PNP=y +# CONFIG_MTD is not set CONFIG_I2C_ALGOBIT=y -# CONFIG_ARCH_OMAP2 is not set -CONFIG_SERIO_SERPORT=y +CONFIG_BITREVERSE=y +CONFIG_FONT_SUPPORT=y +CONFIG_I2C=y # CONFIG_MIPS is not set -CONFIG_EFI=y -# CONFIG_SOC_DRA7XX is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_SND_FM801 is not set +# CONFIG_STA2X11 is not set +# CONFIG_PWM_OMAP_DMTIMER is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_BLACKFIN is not set +# CONFIG_ARCH_OMAP2 is not set +# CONFIG_REGULATOR is not set +# CONFIG_MULTIPLEXER is not set +CONFIG_COMMON_CLK=y +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_OMAP4 is not set +CONFIG_NEW_LEDS=y +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_STM32 is not set +# CONFIG_ARCH_MEDIATEK is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_INET=y +# CONFIG_SOC_IMX27 is not set +CONFIG_X86=y +# CONFIG_SOC_EXYNOS4212 is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_VIDEO_V4L1 is not set +# CONFIG_OMAP2_VRFB is not set +CONFIG_INPUT=y +# CONFIG_RPMSG is not set +# CONFIG_MTK_IOMMU is not set +CONFIG_SND_SOC=y +CONFIG_SERIO_SERPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_OMAP_IOMMU is not set +# CONFIG_ARCH_OMAP2PLUS is not set +CONFIG_MMU=y +# CONFIG_SOC_EXYNOS5250 is not set +# CONFIG_REGMAP_MMIO is not set +CONFIG_RATIONAL=y +# CONFIG_SND_ISA is not set +CONFIG_HAS_IOMEM=y +CONFIG_HDMI=y +# CONFIG_SOC_EXYNOS4412 is not set +# CONFIG_ARCH_AT91 is not set # CONFIG_VIDEO_KERNEL_VERSION is not set +CONFIG_RC_CORE=m +CONFIG_RC_MAP=m +CONFIG_RC_DECODERS=y +CONFIG_LIRC=m +CONFIG_IR_LIRC_CODEC=m +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_SHARP_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_XMP_DECODER=m +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_ENE=m +CONFIG_IR_IMON=m +CONFIG_IR_MCEUSB=m +CONFIG_IR_ITE_CIR=m +CONFIG_IR_FINTEK=m +CONFIG_IR_NUVOTON=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_WINBOND_CIR=m +CONFIG_IR_IGORPLUGUSB=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_RC_LOOPBACK=m +CONFIG_IR_SERIAL=m +CONFIG_IR_SERIAL_TRANSMITTER=y +# CONFIG_IR_SIR is not set CONFIG_MEDIA_SUPPORT=m # # Multimedia core support # -CONFIG_MEDIA_CAMERA_SUPPORT=y +# CONFIG_MEDIA_CAMERA_SUPPORT is not set CONFIG_MEDIA_ANALOG_TV_SUPPORT=y CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y # CONFIG_MEDIA_RADIO_SUPPORT is not set # CONFIG_MEDIA_SDR_SUPPORT is not set -CONFIG_MEDIA_RC_SUPPORT=y # CONFIG_MEDIA_CEC_SUPPORT is not set # CONFIG_MEDIA_CONTROLLER is not set CONFIG_VIDEO_DEV=m @@ -173,54 +214,8 @@ CONFIG_DVB_DYNAMIC_MINORS=y # # Media drivers # -CONFIG_RC_CORE=m -CONFIG_RC_MAP=m -CONFIG_RC_DECODERS=y -CONFIG_LIRC=m -CONFIG_IR_LIRC_CODEC=m -CONFIG_IR_NEC_DECODER=m -CONFIG_IR_RC5_DECODER=m -CONFIG_IR_RC6_DECODER=m -CONFIG_IR_JVC_DECODER=m -CONFIG_IR_SONY_DECODER=m -CONFIG_IR_SANYO_DECODER=m -CONFIG_IR_SHARP_DECODER=m -CONFIG_IR_MCE_KBD_DECODER=m -CONFIG_IR_XMP_DECODER=m -CONFIG_RC_DEVICES=y -CONFIG_RC_ATI_REMOTE=m -CONFIG_IR_ENE=m -CONFIG_IR_HIX5HD2=m -CONFIG_IR_IMON=m -CONFIG_IR_MCEUSB=m -CONFIG_IR_ITE_CIR=m -CONFIG_IR_FINTEK=m -CONFIG_IR_NUVOTON=m -CONFIG_IR_REDRAT3=m -CONFIG_IR_STREAMZAP=m -CONFIG_IR_WINBOND_CIR=m -CONFIG_IR_IGORPLUGUSB=m -CONFIG_IR_IGUANA=m -CONFIG_IR_TTUSBIR=m -CONFIG_RC_LOOPBACK=m -CONFIG_IR_GPIO_CIR=m -CONFIG_IR_SERIAL=m -CONFIG_IR_SERIAL_TRANSMITTER=y -# CONFIG_IR_SIR is not set CONFIG_MEDIA_USB_SUPPORT=y -# -# Webcam devices -# -# CONFIG_USB_VIDEO_CLASS is not set -# CONFIG_USB_GSPCA is not set -# CONFIG_USB_PWC is not set -# CONFIG_VIDEO_CPIA2 is not set -# CONFIG_USB_ZR364XX is not set -# CONFIG_USB_STKWEBCAM is not set -# CONFIG_USB_S2255 is not set -# CONFIG_VIDEO_USBTV is not set - # # Analog TV USB devices # @@ -295,6 +290,7 @@ CONFIG_DVB_USB_TBS5881=m CONFIG_DVB_USB_TBS5520=m CONFIG_DVB_USB_TBS5927=m CONFIG_DVB_USB_TBS5520SE=m +CONFIG_DVB_USB_TBS5580=m CONFIG_DVB_USB_CYCITV=m CONFIG_DVB_USB_V2=m CONFIG_DVB_USB_AF9015=m @@ -327,15 +323,6 @@ CONFIG_VIDEO_EM28XX_DVB=m CONFIG_VIDEO_EM28XX_RC=m CONFIG_MEDIA_PCI_SUPPORT=y -# -# Media capture support -# -# CONFIG_VIDEO_SOLO6X10 is not set -# CONFIG_VIDEO_TW5864 is not set -# CONFIG_VIDEO_TW68 is not set -# CONFIG_VIDEO_TW686X is not set -# CONFIG_VIDEO_ZORAN is not set - # # Media capture/analog TV support # @@ -394,11 +381,9 @@ CONFIG_DVB_MANTIS=m CONFIG_DVB_HOPPER=m CONFIG_DVB_NGENE=m CONFIG_DVB_DDBRIDGE=m +# CONFIG_DVB_DDBRIDGE_MSIENABLE is not set CONFIG_DVB_SMIPCIE=m CONFIG_DVB_TBSECP3=m -# CONFIG_V4L_PLATFORM_DRIVERS is not set -# CONFIG_V4L_MEM2MEM_DRIVERS is not set -# CONFIG_V4L_TEST_DRIVERS is not set # CONFIG_DVB_PLATFORM_DRIVERS is not set # @@ -467,9 +452,6 @@ CONFIG_VIDEO_CX25840=m # # Camera sensor devices # -CONFIG_VIDEO_OV2640=m -CONFIG_VIDEO_OV7640=m -CONFIG_VIDEO_MT9V011=m # # Flash devices @@ -484,6 +466,10 @@ CONFIG_VIDEO_MT9V011=m # CONFIG_VIDEO_SAA6752HS=m +# +# SDR tuner chips +# + # # Miscellaneous helper chips # @@ -493,6 +479,7 @@ CONFIG_VIDEO_SAA6752HS=m # CONFIG_MEDIA_TUNER=m CONFIG_MEDIA_TUNER_SIMPLE=m +CONFIG_MEDIA_TUNER_TDA18250=m CONFIG_MEDIA_TUNER_TDA8290=m CONFIG_MEDIA_TUNER_TDA827X=m CONFIG_MEDIA_TUNER_TDA18271=m @@ -534,7 +521,10 @@ CONFIG_MEDIA_TUNER_R848=m CONFIG_DVB_STB0899=m CONFIG_DVB_STB6100=m CONFIG_DVB_STV090x=m +CONFIG_DVB_STV0910=m CONFIG_DVB_STV6110x=m +CONFIG_DVB_STV6111=m +CONFIG_DVB_MXL5XX=m CONFIG_DVB_M88DS3103=m # @@ -598,6 +588,7 @@ CONFIG_DVB_AF9013=m CONFIG_DVB_EC100=m CONFIG_DVB_STV0367=m CONFIG_DVB_CXD2820R=m +CONFIG_DVB_CXD2841ER=m CONFIG_DVB_RTL2830=m CONFIG_DVB_RTL2832=m CONFIG_DVB_SI2168=m @@ -652,6 +643,7 @@ CONFIG_DVB_TUNER_DIB0090=m # SEC control devices for DVB-S # CONFIG_DVB_DRX39XYJ=m +CONFIG_DVB_LNBH25=m CONFIG_DVB_LNBP21=m CONFIG_DVB_LNBP22=m CONFIG_DVB_ISL6405=m @@ -668,8 +660,8 @@ CONFIG_DVB_M88RS2000=m CONFIG_DVB_AF9033=m CONFIG_DVB_TAS2101=m CONFIG_DVB_AVL6882=m -CONFIG_DVB_STV0910=m -CONFIG_DVB_MXL5XX=m +CONFIG_DVB_STV091X=m +CONFIG_DVB_MXL58X=m CONFIG_DVB_SI2183=m # diff --git a/packages/linux-drivers/media_build/config/usb.config b/packages/linux-driver-addons/dvb/crazycat/config/usb.config similarity index 94% rename from packages/linux-drivers/media_build/config/usb.config rename to packages/linux-driver-addons/dvb/crazycat/config/usb.config index eba2a84c3b..0b85d855d0 100644 --- a/packages/linux-drivers/media_build/config/usb.config +++ b/packages/linux-driver-addons/dvb/crazycat/config/usb.config @@ -2,150 +2,191 @@ # Automatically generated file; DO NOT EDIT. # V4L/DVB menu # -# CONFIG_SOC_EXYNOS4212 is not set -# CONFIG_PWM_OMAP_DMTIMER is not set -# CONFIG_ARCH_OMAP3 is not set -# CONFIG_BROKEN is not set -# CONFIG_ARM_DMA_USE_IOMMU is not set -CONFIG_HDMI=y -CONFIG_LEDS_CLASS_FLASH=y -CONFIG_PCI_MSI=y -# CONFIG_MTK_IOMMU_V1 is not set -# CONFIG_RPMSG is not set -# CONFIG_PLAT_S3C24XX is not set -# CONFIG_FB_VIA is not set -# CONFIG_SND_FM801 is not set -# CONFIG_I2C_GPIO is not set -CONFIG_I2C=y +# CONFIG_ARCH_STI is not set +# CONFIG_TI_ST is not set CONFIG_HAVE_CLK=y -# CONFIG_REGULATOR is not set -CONFIG_PM=y -# CONFIG_ARCH_MEDIATEK is not set -# CONFIG_ISA is not set -CONFIG_DEBUG_FS=y -CONFIG_MODULES=y -# CONFIG_SPI is not set +CONFIG_I2C_MUX=m +# CONFIG_PLAT_M32700UT is not set +# CONFIG_METAG is not set +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_MXC is not set # CONFIG_MFD_WL1273_CORE is not set -CONFIG_SND_PCM=y -CONFIG_MMU=y -CONFIG_MFD_CORE=y -CONFIG_MMC=y -CONFIG_USB=y -CONFIG_SYSFS=y +CONFIG_VIRT_TO_BUS=y +CONFIG_LEDS_CLASS=y # CONFIG_MTD_COMPLEX_MAPPINGS is not set -CONFIG_FONT_SUPPORT=y -CONFIG_RATIONAL=y +CONFIG_TTY=y +CONFIG_ISA_DMA_API=y +CONFIG_USB_ACM=m +# CONFIG_SPARC32 is not set +# CONFIG_ARCH_MMP is not set +CONFIG_PCI_MSI=y +# CONFIG_MFD_TIMBERDALE is not set +CONFIG_FB=y +CONFIG_HAS_DMA=y +# CONFIG_SOC_DRA7XX is not set +# CONFIG_IMX_IPUV3_CORE is not set +CONFIG_NET=y +# CONFIG_PXA27x is not set +# CONFIG_ARCH_DAVINCI_DM365 is not set +# CONFIG_OF is not set +# CONFIG_SRAM is not set +# CONFIG_SND_SOC_SI476X is not set +# CONFIG_SONY_LAPTOP is not set +# CONFIG_QCOM_MDT_LOADER is not set +# CONFIG_ARCH_RENESAS is not set +# CONFIG_SPARC64 is not set +# CONFIG_ARCH_S3C64XX is not set +CONFIG_FONT_8x16=y +# CONFIG_FB_OMAP2 is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_PREEMPT is not set +# CONFIG_ARCH_S5PV210 is not set CONFIG_CRC32=y +CONFIG_SND=y +CONFIG_RFKILL=m +# CONFIG_M32R is not set +CONFIG_PINCTRL=y +# CONFIG_SG_SPLIT is not set +CONFIG_MODULES=y +CONFIG_REGMAP_I2C=y +# CONFIG_GENERIC_PHY is not set +# CONFIG_ARCH_ZX is not set +# CONFIG_MFD_SI476X_CORE is not set +CONFIG_DEBUG_FS=y +# CONFIG_FB_VIA is not set +CONFIG_REGMAP=y +CONFIG_PCI=y +# CONFIG_ARCH_MULTIPLATFORM is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_QCOM_SCM is not set +# CONFIG_PLAT_S5P is not set CONFIG_SERIO=y # CONFIG_ARCH_SUNXI is not set -CONFIG_USB_ARCH_HAS_HCD=y -# CONFIG_PLAT_S5P is not set -# CONFIG_OMAP_DM_TIMER is not set -# CONFIG_MFD_TIMBERDALE is not set -# CONFIG_SND_ISA is not set -CONFIG_ISA_DMA_API=y -CONFIG_NEW_LEDS=y -CONFIG_PCI=y -# CONFIG_ARCH_OMAP2PLUS is not set -# CONFIG_VIDEO_V4L1 is not set -CONFIG_FW_LOADER=y -CONFIG_DMA_ENGINE=y -# CONFIG_GENERIC_PHY is not set -CONFIG_HAS_IOMEM=y -CONFIG_NET=y -# CONFIG_ARCH_EXYNOS is not set -# CONFIG_SOC_AM43XX is not set -CONFIG_INET=y -# CONFIG_SPARC32 is not set -# CONFIG_TI_ST is not set -# CONFIG_STA2X11 is not set -# CONFIG_MTD is not set -CONFIG_USB_ACM=m -# CONFIG_ARCH_STM32 is not set -CONFIG_SND=y -CONFIG_PINCTRL=y -CONFIG_PNP=y -# CONFIG_SRAM is not set -# CONFIG_SG_SPLIT is not set -# CONFIG_SONY_LAPTOP is not set -# CONFIG_ARCH_S5PV210 is not set -CONFIG_COMMON_CLK=y -# CONFIG_SOC_IMX27 is not set -CONFIG_INPUT=y -# CONFIG_OF is not set -CONFIG_FONT_8x16=y -# CONFIG_SOC_EXYNOS5250 is not set -CONFIG_ACPI=y -# CONFIG_ARCH_MMP is not set -CONFIG_FIREWIRE=m -# CONFIG_PLAT_M32700UT is not set -CONFIG_DMA_SHARED_BUFFER=y -# CONFIG_PPC_MPC512x is not set -CONFIG_X86=y -# CONFIG_SND_MIRO is not set -# CONFIG_OMAP_IOMMU is not set -# CONFIG_ARCH_MULTIPLATFORM is not set -CONFIG_VIRT_TO_BUS=y -# CONFIG_ARCH_S3C64XX is not set -# CONFIG_ARCH_SHMOBILE is not set -# CONFIG_PXA27x is not set -# CONFIG_MFD_SI476X_CORE is not set -# CONFIG_ARM64 is not set -CONFIG_GPIOLIB=y -# CONFIG_FB_OMAP2 is not set # CONFIG_GENERIC_ALLOCATOR is not set -# CONFIG_OMAP2_VRFB is not set -CONFIG_BITREVERSE=y -# CONFIG_BLACKFIN is not set -# CONFIG_REGMAP_MMIO is not set -CONFIG_RFKILL=m -CONFIG_FB_CFB_FILLRECT=y -CONFIG_TTY=y -# CONFIG_SPI_MASTER is not set -CONFIG_LEDS_CLASS=y -# CONFIG_MTK_IOMMU is not set -CONFIG_FB_CFB_IMAGEBLIT=y -# CONFIG_METAG is not set -CONFIG_REGMAP=y -# CONFIG_ARCH_MESON is not set -# CONFIG_M32R is not set -CONFIG_SND_SOC=y -# CONFIG_ARCH_OMAP4 is not set -CONFIG_HAS_DMA=y -# CONFIG_MFD_SYSCON is not set -CONFIG_INPUT_EVDEV=y -# CONFIG_ARCH_AT91 is not set -# CONFIG_SPARC64 is not set -# CONFIG_ARCH_RENESAS is not set +# CONFIG_ISA is not set +# CONFIG_PWM is not set +CONFIG_SYSFS=y +# CONFIG_BROKEN is not set +CONFIG_MMC=y +# CONFIG_MTK_IOMMU_V1 is not set +# CONFIG_ARCH_TANGO is not set +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_GPIOLIB=y +# CONFIG_ARM64 is not set +# CONFIG_ARM_DMA_USE_IOMMU is not set +# CONFIG_PPC_MPC512x is not set +# CONFIG_OMAP_DM_TIMER is not set +# CONFIG_ARCH_EXYNOS is not set # CONFIG_ALPHA is not set +CONFIG_LEDS_CLASS_FLASH=y CONFIG_FB_CFB_COPYAREA=y -# CONFIG_ARCH_DAVINCI is not set -# CONFIG_ARCH_MXC is not set -CONFIG_FB=y -# CONFIG_ARCH_DAVINCI_DM365 is not set -# CONFIG_SOC_EXYNOS4412 is not set -# CONFIG_ARCH_STI is not set -CONFIG_I2C_MUX=m -# CONFIG_SND_SOC_SI476X is not set -CONFIG_REGMAP_I2C=y +CONFIG_INPUT_EVDEV=y +CONFIG_FIREWIRE=m +CONFIG_MFD_CORE=y +CONFIG_PM=y +CONFIG_DMA_ENGINE=y +CONFIG_SND_PCM=y +# CONFIG_PLAT_S3C24XX is not set +# CONFIG_SND_MIRO is not set +# CONFIG_SPI is not set +CONFIG_FW_LOADER=y +# CONFIG_MFD_SYSCON is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_IOMMU_DMA is not set +CONFIG_USB=y +CONFIG_PNP=y +# CONFIG_MTD is not set CONFIG_I2C_ALGOBIT=y -# CONFIG_ARCH_OMAP2 is not set -CONFIG_SERIO_SERPORT=y +CONFIG_BITREVERSE=y +CONFIG_FONT_SUPPORT=y +CONFIG_I2C=y # CONFIG_MIPS is not set -CONFIG_EFI=y -# CONFIG_SOC_DRA7XX is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_SND_FM801 is not set +# CONFIG_STA2X11 is not set +# CONFIG_PWM_OMAP_DMTIMER is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_BLACKFIN is not set +# CONFIG_ARCH_OMAP2 is not set +# CONFIG_REGULATOR is not set +# CONFIG_MULTIPLEXER is not set +CONFIG_COMMON_CLK=y +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_OMAP4 is not set +CONFIG_NEW_LEDS=y +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_STM32 is not set +# CONFIG_ARCH_MEDIATEK is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_INET=y +# CONFIG_SOC_IMX27 is not set +CONFIG_X86=y +# CONFIG_SOC_EXYNOS4212 is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_VIDEO_V4L1 is not set +# CONFIG_OMAP2_VRFB is not set +CONFIG_INPUT=y +# CONFIG_RPMSG is not set +# CONFIG_MTK_IOMMU is not set +CONFIG_SND_SOC=y +CONFIG_SERIO_SERPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_OMAP_IOMMU is not set +# CONFIG_ARCH_OMAP2PLUS is not set +CONFIG_MMU=y +# CONFIG_SOC_EXYNOS5250 is not set +# CONFIG_REGMAP_MMIO is not set +CONFIG_RATIONAL=y +# CONFIG_SND_ISA is not set +CONFIG_HAS_IOMEM=y +CONFIG_HDMI=y +# CONFIG_SOC_EXYNOS4412 is not set +# CONFIG_ARCH_AT91 is not set # CONFIG_VIDEO_KERNEL_VERSION is not set +CONFIG_RC_CORE=m +CONFIG_RC_MAP=m +CONFIG_RC_DECODERS=y +CONFIG_LIRC=m +CONFIG_IR_LIRC_CODEC=m +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_SHARP_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_XMP_DECODER=m +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_ENE=m +CONFIG_IR_IMON=m +CONFIG_IR_MCEUSB=m +CONFIG_IR_ITE_CIR=m +CONFIG_IR_FINTEK=m +CONFIG_IR_NUVOTON=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_WINBOND_CIR=m +CONFIG_IR_IGORPLUGUSB=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_RC_LOOPBACK=m +CONFIG_IR_SERIAL=m +CONFIG_IR_SERIAL_TRANSMITTER=y +# CONFIG_IR_SIR is not set CONFIG_MEDIA_SUPPORT=m # # Multimedia core support # -CONFIG_MEDIA_CAMERA_SUPPORT=y +# CONFIG_MEDIA_CAMERA_SUPPORT is not set CONFIG_MEDIA_ANALOG_TV_SUPPORT=y CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y # CONFIG_MEDIA_RADIO_SUPPORT is not set # CONFIG_MEDIA_SDR_SUPPORT is not set -CONFIG_MEDIA_RC_SUPPORT=y # CONFIG_MEDIA_CEC_SUPPORT is not set # CONFIG_MEDIA_CONTROLLER is not set CONFIG_VIDEO_DEV=m @@ -170,54 +211,8 @@ CONFIG_DVB_DYNAMIC_MINORS=y # # Media drivers # -CONFIG_RC_CORE=m -CONFIG_RC_MAP=m -CONFIG_RC_DECODERS=y -CONFIG_LIRC=m -CONFIG_IR_LIRC_CODEC=m -CONFIG_IR_NEC_DECODER=m -CONFIG_IR_RC5_DECODER=m -CONFIG_IR_RC6_DECODER=m -CONFIG_IR_JVC_DECODER=m -CONFIG_IR_SONY_DECODER=m -CONFIG_IR_SANYO_DECODER=m -CONFIG_IR_SHARP_DECODER=m -CONFIG_IR_MCE_KBD_DECODER=m -CONFIG_IR_XMP_DECODER=m -CONFIG_RC_DEVICES=y -CONFIG_RC_ATI_REMOTE=m -CONFIG_IR_ENE=m -CONFIG_IR_HIX5HD2=m -CONFIG_IR_IMON=m -CONFIG_IR_MCEUSB=m -CONFIG_IR_ITE_CIR=m -CONFIG_IR_FINTEK=m -CONFIG_IR_NUVOTON=m -CONFIG_IR_REDRAT3=m -CONFIG_IR_STREAMZAP=m -CONFIG_IR_WINBOND_CIR=m -CONFIG_IR_IGORPLUGUSB=m -CONFIG_IR_IGUANA=m -CONFIG_IR_TTUSBIR=m -CONFIG_RC_LOOPBACK=m -CONFIG_IR_GPIO_CIR=m -CONFIG_IR_SERIAL=m -CONFIG_IR_SERIAL_TRANSMITTER=y -# CONFIG_IR_SIR is not set CONFIG_MEDIA_USB_SUPPORT=y -# -# Webcam devices -# -# CONFIG_USB_VIDEO_CLASS is not set -# CONFIG_USB_GSPCA is not set -# CONFIG_USB_PWC is not set -# CONFIG_VIDEO_CPIA2 is not set -# CONFIG_USB_ZR364XX is not set -# CONFIG_USB_STKWEBCAM is not set -# CONFIG_USB_S2255 is not set -# CONFIG_VIDEO_USBTV is not set - # # Analog TV USB devices # @@ -292,6 +287,7 @@ CONFIG_DVB_USB_TBS5881=m CONFIG_DVB_USB_TBS5520=m CONFIG_DVB_USB_TBS5927=m CONFIG_DVB_USB_TBS5520SE=m +CONFIG_DVB_USB_TBS5580=m CONFIG_DVB_USB_CYCITV=m CONFIG_DVB_USB_V2=m CONFIG_DVB_USB_AF9015=m @@ -323,9 +319,6 @@ CONFIG_VIDEO_EM28XX_V4L2=m CONFIG_VIDEO_EM28XX_DVB=m CONFIG_VIDEO_EM28XX_RC=m # CONFIG_MEDIA_PCI_SUPPORT is not set -# CONFIG_V4L_PLATFORM_DRIVERS is not set -# CONFIG_V4L_MEM2MEM_DRIVERS is not set -# CONFIG_V4L_TEST_DRIVERS is not set # CONFIG_DVB_PLATFORM_DRIVERS is not set # @@ -390,9 +383,6 @@ CONFIG_VIDEO_CX25840=m # # Camera sensor devices # -CONFIG_VIDEO_OV2640=m -CONFIG_VIDEO_OV7640=m -CONFIG_VIDEO_MT9V011=m # # Flash devices @@ -406,6 +396,10 @@ CONFIG_VIDEO_MT9V011=m # Audio/Video compression chips # +# +# SDR tuner chips +# + # # Miscellaneous helper chips # @@ -415,6 +409,7 @@ CONFIG_VIDEO_MT9V011=m # CONFIG_MEDIA_TUNER=m CONFIG_MEDIA_TUNER_SIMPLE=m +CONFIG_MEDIA_TUNER_TDA18250=m CONFIG_MEDIA_TUNER_TDA8290=m CONFIG_MEDIA_TUNER_TDA827X=m CONFIG_MEDIA_TUNER_TDA18271=m @@ -573,7 +568,7 @@ CONFIG_DVB_M88RS2000=m CONFIG_DVB_AF9033=m CONFIG_DVB_TAS2101=m CONFIG_DVB_AVL6882=m -CONFIG_DVB_STV0910=m +CONFIG_DVB_STV091X=m CONFIG_DVB_SI2183=m # @@ -589,8 +584,3 @@ CONFIG_MISC_DEVICES=y # CONFIG_ALTERA_STAPL=m # CONFIG_STAGING is not set - -# RPi DVB Hat -CONFIG_DVB_CXD2820R=m -CONFIG_DVB_CXD2841ER=m -CONFIG_DVB_CXD2880=m diff --git a/packages/linux-driver-addons/dvb/crazycat/icon/icon.png b/packages/linux-driver-addons/dvb/crazycat/icon/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..22a6c6a0d90344f27d68561d8d44115debdfe89a GIT binary patch literal 68596 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 literal 0 HcmV?d00001 diff --git a/packages/linux-driver-addons/dvb/crazycat/package.mk b/packages/linux-driver-addons/dvb/crazycat/package.mk new file mode 100644 index 0000000000..ebbb4e42f6 --- /dev/null +++ b/packages/linux-driver-addons/dvb/crazycat/package.mk @@ -0,0 +1,63 @@ +################################################################################ +# 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="crazycat" +PKG_VERSION="2017-11-13" +PKG_SHA256="14d951eb8d40cee40d601d7c737bca07171d8b4f201d63d5e70a24c4841f9d73" +PKG_ARCH="any" +PKG_LICENSE="GPL" +PKG_SITE="https://github.com/crazycat69/linux_media" +PKG_URL="$DISTRO_SRC/$PKG_NAME-$PKG_VERSION.tar.xz" +PKG_DEPENDS_TARGET="toolchain linux" +PKG_BUILD_DEPENDS_TARGET="toolchain linux" +PKG_NEED_UNPACK="$LINUX_DEPENDS" +PKG_SECTION="driver.dvb" +PKG_LONGDESC="DVB driver for TBS cards with CrazyCats additions." + +PKG_IS_ADDON="yes" +PKG_ADDON_IS_STANDALONE="yes" +PKG_ADDON_NAME="DVB drivers for TBS (CrazyCat)" +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() { + make untar + + # copy config file + if [ "$PROJECT" = Generic ]; then + if [ -f $PKG_DIR/config/generic.config ]; then + cp $PKG_DIR/config/generic.config v4l/.config + fi + else + if [ -f $PKG_DIR/config/usb.config ]; then + cp $PKG_DIR/config/usb.config v4l/.config + fi + fi + + # add menuconfig to edit .config + make VER=$KERNEL_VER SRCDIR=$(kernel_path) +} + +makeinstall_target() { + install_driver_addon_files "$PKG_BUILD/v4l/" +} diff --git a/packages/linux-drivers/media_build/patches/media_build-01-remove-rmmod.pl.patch b/packages/linux-driver-addons/dvb/crazycat/patches/driver.dvb.crazycat-01-remove-rmmod.pl.patch similarity index 100% rename from packages/linux-drivers/media_build/patches/media_build-01-remove-rmmod.pl.patch rename to packages/linux-driver-addons/dvb/crazycat/patches/driver.dvb.crazycat-01-remove-rmmod.pl.patch diff --git a/packages/linux-driver-addons/dvb/crazycat/patches/driver.dvb.crazycat-02-add-to-backports.patch b/packages/linux-driver-addons/dvb/crazycat/patches/driver.dvb.crazycat-02-add-to-backports.patch new file mode 100644 index 0000000000..8eec833bde --- /dev/null +++ b/packages/linux-driver-addons/dvb/crazycat/patches/driver.dvb.crazycat-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 linux-202-lnbp22_patch_for_more_power_if_rotor.patch ++add linux-220-Xbox-One-DVB-T2-stick-support.patch + + [4.12.255] + add v4.12_revert_solo6x10_copykerneluser.patch diff --git a/packages/linux-driver-addons/dvb/crazycat/source/default.py b/packages/linux-driver-addons/dvb/crazycat/source/default.py new file mode 100644 index 0000000000..fe3ba645a6 --- /dev/null +++ b/packages/linux-driver-addons/dvb/crazycat/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-drivers/media_build/sources/backports/linux-202-lnbp22_patch_for_more_power_if_rotor.patch b/packages/linux-driver-addons/dvb/crazycat/sources/backports/linux-202-lnbp22_patch_for_more_power_if_rotor.patch similarity index 100% rename from packages/linux-drivers/media_build/sources/backports/linux-202-lnbp22_patch_for_more_power_if_rotor.patch rename to packages/linux-driver-addons/dvb/crazycat/sources/backports/linux-202-lnbp22_patch_for_more_power_if_rotor.patch diff --git a/packages/linux-driver-addons/dvb/crazycat/sources/backports/linux-220-Xbox-One-DVB-T2-stick-support.patch b/packages/linux-driver-addons/dvb/crazycat/sources/backports/linux-220-Xbox-One-DVB-T2-stick-support.patch new file mode 100644 index 0000000000..615c8d8cfb --- /dev/null +++ b/packages/linux-driver-addons/dvb/crazycat/sources/backports/linux-220-Xbox-One-DVB-T2-stick-support.patch @@ -0,0 +1,1386 @@ +From b8ca04e7037433d13b6a526123d16493eff74bd0 Mon Sep 17 00:00:00 2001 +From: Olli Salonen +Date: Thu, 23 Nov 2017 08:36:10 +0200 +Subject: Xbox ONE DVB-C/T/T2 USB Tuner support + +diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h +index 7678319..e1a043b 100644 +--- a/drivers/media/dvb-core/dvb-usb-ids.h ++++ b/drivers/media/dvb-core/dvb-usb-ids.h +@@ -80,6 +80,7 @@ + #define USB_VID_AZUREWAVE 0x13d3 + #define USB_VID_TECHNISAT 0x14f7 + #define USB_VID_HAMA 0x147f ++#define USB_VID_MICROSOFT 0x045e + + /* Product IDs */ + #define USB_PID_ADSTECH_USB2_COLD 0xa333 +@@ -418,4 +419,5 @@ + #define USB_PID_WINTV_SOLOHD 0x0264 + #define USB_PID_EVOLVEO_XTRATV_STICK 0xa115 + #define USB_PID_HAMA_DVBT_HYBRID 0x2758 ++#define USB_PID_XBOX_ONE_TUNER 0x02d5 + #endif +diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig +index 1dc7bb6..0a714ba 100644 +--- a/drivers/media/tuners/Kconfig ++++ b/drivers/media/tuners/Kconfig +@@ -26,6 +26,13 @@ config MEDIA_TUNER_SIMPLE + help + Say Y here to include support for various simple tuners. + ++config MEDIA_TUNER_TDA18250 ++ tristate "NXP TDA18250 silicon tuner" ++ depends on MEDIA_SUPPORT && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y here to include support for TDA18250 tuner. ++ + config MEDIA_TUNER_TDA8290 + tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo" + depends on MEDIA_SUPPORT && I2C +diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile +index ba382f9..aee8801 100644 +--- a/drivers/media/tuners/Makefile ++++ b/drivers/media/tuners/Makefile +@@ -45,6 +45,7 @@ obj-$(CONFIG_MEDIA_TUNER_M88RS6000T) += m88rs6000t.o + obj-$(CONFIG_MEDIA_TUNER_AV201X) += av201x.o + obj-$(CONFIG_MEDIA_TUNER_R848) += r848.o + obj-$(CONFIG_MEDIA_TUNER_STV6120) += stv6120.o ++obj-$(CONFIG_MEDIA_TUNER_TDA18250) += tda18250.o + + ccflags-y += -I$(srctree)/drivers/media/dvb-core + ccflags-y += -I$(srctree)/drivers/media/dvb-frontends +diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c +new file mode 100644 +index 0000000..20d12b0 +--- /dev/null ++++ b/drivers/media/tuners/tda18250.c +@@ -0,0 +1,902 @@ ++/* ++ * NXP TDA18250 silicon tuner driver ++ * ++ * Copyright (C) 2017 Olli Salonen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include "tda18250_priv.h" ++#include ++ ++static const struct dvb_tuner_ops tda18250_ops; ++ ++static int tda18250_power_control(struct dvb_frontend *fe, ++ unsigned int power_state) ++{ ++ struct i2c_client *client = fe->tuner_priv; ++ struct tda18250_dev *dev = i2c_get_clientdata(client); ++ int ret; ++ unsigned int utmp; ++ ++ dev_dbg(&client->dev, "power state: %d", power_state); ++ ++ switch (power_state) { ++ case TDA18250_POWER_NORMAL: ++ ret = regmap_write_bits(dev->regmap, R06_POWER2, 0x07, 0x00); ++ if (ret) ++ goto err; ++ ret = regmap_write_bits(dev->regmap, R25_REF, 0xc0, 0xc0); ++ if (ret) ++ goto err; ++ break; ++ case TDA18250_POWER_STANDBY: ++ if (dev->loopthrough) { ++ ret = regmap_write_bits(dev->regmap, ++ R25_REF, 0xc0, 0x80); ++ if (ret) ++ goto err; ++ ret = regmap_write_bits(dev->regmap, ++ R06_POWER2, 0x07, 0x02); ++ if (ret) ++ goto err; ++ ret = regmap_write_bits(dev->regmap, ++ R10_LT1, 0x80, 0x00); ++ if (ret) ++ goto err; ++ } else { ++ ret = regmap_write_bits(dev->regmap, ++ R25_REF, 0xc0, 0x80); ++ if (ret) ++ goto err; ++ ret = regmap_write_bits(dev->regmap, ++ R06_POWER2, 0x07, 0x01); ++ if (ret) ++ goto err; ++ ret = regmap_read(dev->regmap, ++ R0D_AGC12, &utmp); ++ if (ret) ++ goto err; ++ ret = regmap_write_bits(dev->regmap, ++ R0D_AGC12, 0x03, 0x03); ++ if (ret) ++ goto err; ++ ret = regmap_write_bits(dev->regmap, ++ R10_LT1, 0x80, 0x80); ++ if (ret) ++ goto err; ++ ret = regmap_write_bits(dev->regmap, ++ R0D_AGC12, 0x03, utmp & 0x03); ++ if (ret) ++ goto err; ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ return 0; ++err: ++ return ret; ++} ++ ++static int tda18250_wait_for_irq(struct dvb_frontend *fe, ++ int maxwait, int step, u8 irq) ++{ ++ struct i2c_client *client = fe->tuner_priv; ++ struct tda18250_dev *dev = i2c_get_clientdata(client); ++ int ret; ++ unsigned long timeout; ++ bool triggered; ++ unsigned int utmp; ++ ++ triggered = false; ++ timeout = jiffies + msecs_to_jiffies(maxwait); ++ while (!time_after(jiffies, timeout)) { ++ // check for the IRQ ++ ret = regmap_read(dev->regmap, R08_IRQ1, &utmp); ++ if (ret) ++ goto err; ++ if ((utmp & irq) == irq) { ++ triggered = true; ++ break; ++ } ++ msleep(step); ++ } ++ ++ dev_dbg(&client->dev, "waited IRQ (0x%02x) %d ms, triggered: %s", irq, ++ jiffies_to_msecs(jiffies) - ++ (jiffies_to_msecs(timeout) - maxwait), ++ triggered ? "true" : "false"); ++ ++ if (!triggered) ++ return -ETIMEDOUT; ++ ++ return 0; ++err: ++ return ret; ++} ++ ++static int tda18250_init(struct dvb_frontend *fe) ++{ ++ struct i2c_client *client = fe->tuner_priv; ++ struct tda18250_dev *dev = i2c_get_clientdata(client); ++ int ret, i; ++ ++ /* default values for various regs */ ++ static const u8 init_regs[][2] = { ++ { R0C_AGC11, 0xc7 }, ++ { R0D_AGC12, 0x5d }, ++ { R0E_AGC13, 0x40 }, ++ { R0F_AGC14, 0x0e }, ++ { R10_LT1, 0x47 }, ++ { R11_LT2, 0x4e }, ++ { R12_AGC21, 0x26 }, ++ { R13_AGC22, 0x60 }, ++ { R18_AGC32, 0x37 }, ++ { R19_AGC33, 0x09 }, ++ { R1A_AGCK, 0x00 }, ++ { R1E_WI_FI, 0x29 }, ++ { R1F_RF_BPF, 0x06 }, ++ { R20_IR_MIX, 0xc6 }, ++ { R21_IF_AGC, 0x00 }, ++ { R2C_PS1, 0x75 }, ++ { R2D_PS2, 0x06 }, ++ { R2E_PS3, 0x07 }, ++ { R30_RSSI2, 0x0e }, ++ { R31_IRQ_CTRL, 0x00 }, ++ { R39_SD5, 0x00 }, ++ { R3B_REGU, 0x55 }, ++ { R3C_RCCAL1, 0xa7 }, ++ { R3F_IRCAL2, 0x85 }, ++ { R40_IRCAL3, 0x87 }, ++ { R41_IRCAL4, 0xc0 }, ++ { R43_PD1, 0x40 }, ++ { R44_PD2, 0xc0 }, ++ { R46_CPUMP, 0x0c }, ++ { R47_LNAPOL, 0x64 }, ++ { R4B_XTALOSC1, 0x30 }, ++ { R59_AGC2_UP2, 0x05 }, ++ { R5B_AGC_AUTO, 0x07 }, ++ { R5C_AGC_DEBUG, 0x00 }, ++ }; ++ ++ /* crystal related regs depend on frequency */ ++ static const u8 xtal_regs[][5] = { ++ /* reg: 4d 4e 4f 50 51 */ ++ [TDA18250_XTAL_FREQ_16MHZ] = { 0x3e, 0x80, 0x50, 0x00, 0x20 }, ++ [TDA18250_XTAL_FREQ_24MHZ] = { 0x5d, 0xc0, 0xec, 0x00, 0x18 }, ++ [TDA18250_XTAL_FREQ_25MHZ] = { 0x61, 0xa8, 0xec, 0x80, 0x19 }, ++ [TDA18250_XTAL_FREQ_27MHZ] = { 0x69, 0x78, 0x8d, 0x80, 0x1b }, ++ [TDA18250_XTAL_FREQ_30MHZ] = { 0x75, 0x30, 0x8f, 0x00, 0x1e }, ++ }; ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ ret = tda18250_power_control(fe, TDA18250_POWER_NORMAL); ++ if (ret) ++ goto err; ++ ++ msleep(20); ++ ++ if (dev->warm) ++ goto warm; ++ ++ /* set initial register values */ ++ for (i = 0; i < ARRAY_SIZE(init_regs); i++) { ++ ret = regmap_write(dev->regmap, init_regs[i][0], ++ init_regs[i][1]); ++ if (ret) ++ goto err; ++ } ++ ++ /* set xtal related regs */ ++ ret = regmap_bulk_write(dev->regmap, R4D_XTALFLX1, ++ xtal_regs[dev->xtal_freq], 5); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write_bits(dev->regmap, R10_LT1, 0x80, ++ dev->loopthrough ? 0x00 : 0x80); ++ if (ret) ++ goto err; ++ ++ /* clear IRQ */ ++ ret = regmap_write(dev->regmap, R0A_IRQ3, TDA18250_IRQ_HW_INIT); ++ if (ret) ++ goto err; ++ ++ /* start HW init */ ++ ret = regmap_write(dev->regmap, R2A_MSM1, 0x70); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write(dev->regmap, R2B_MSM2, 0x01); ++ if (ret) ++ goto err; ++ ++ ret = tda18250_wait_for_irq(fe, 500, 10, TDA18250_IRQ_HW_INIT); ++ if (ret) ++ goto err; ++ ++ /* tuner calibration */ ++ ret = regmap_write(dev->regmap, R2A_MSM1, 0x02); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write(dev->regmap, R2B_MSM2, 0x01); ++ if (ret) ++ goto err; ++ ++ ret = tda18250_wait_for_irq(fe, 500, 10, TDA18250_IRQ_CAL); ++ if (ret) ++ goto err; ++ ++ dev->warm = true; ++ ++warm: ++ /* power up LNA */ ++ ret = regmap_write_bits(dev->regmap, R0C_AGC11, 0x80, 0x00); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_dbg(&client->dev, "failed=%d", ret); ++ return ret; ++} ++ ++static int tda18250_set_agc(struct dvb_frontend *fe) ++{ ++ struct i2c_client *client = fe->tuner_priv; ++ struct tda18250_dev *dev = i2c_get_clientdata(client); ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ u8 utmp, utmp2; ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ ret = regmap_write_bits(dev->regmap, R1F_RF_BPF, 0x87, 0x06); ++ if (ret) ++ goto err; ++ ++ utmp = ((c->frequency < 100000000) && ++ ((c->delivery_system == SYS_DVBC_ANNEX_A) || ++ (c->delivery_system == SYS_DVBC_ANNEX_C)) && ++ (c->bandwidth_hz == 6000000)) ? 0x80 : 0x00; ++ ret = regmap_write(dev->regmap, R5A_H3H5, utmp); ++ if (ret) ++ goto err; ++ ++ /* AGC1 */ ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ utmp = 4; ++ break; ++ default: /* DVB-C/QAM */ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ utmp = (c->frequency < 800000000) ? 6 : 4; ++ break; ++ default: /* 7.935 and 8 MHz */ ++ utmp = (c->frequency < 100000000) ? 2 : 3; ++ break; ++ } ++ break; ++ } ++ ++ ret = regmap_write_bits(dev->regmap, R0C_AGC11, 0x07, utmp); ++ if (ret) ++ goto err; ++ ++ /* AGC2 */ ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ utmp = (c->frequency < 320000000) ? 20 : 16; ++ utmp2 = (c->frequency < 320000000) ? 22 : 18; ++ break; ++ default: /* DVB-C/QAM */ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ if (c->frequency < 600000000) { ++ utmp = 18; ++ utmp2 = 22; ++ } else if (c->frequency < 800000000) { ++ utmp = 16; ++ utmp2 = 20; ++ } else { ++ utmp = 14; ++ utmp2 = 16; ++ } ++ break; ++ default: /* 7.935 and 8 MHz */ ++ utmp = (c->frequency < 320000000) ? 16 : 18; ++ utmp2 = (c->frequency < 320000000) ? 18 : 20; ++ break; ++ } ++ break; ++ } ++ ret = regmap_write_bits(dev->regmap, R58_AGC2_UP1, 0x1f, utmp2+8); ++ if (ret) ++ goto err; ++ ret = regmap_write_bits(dev->regmap, R13_AGC22, 0x1f, utmp); ++ if (ret) ++ goto err; ++ ret = regmap_write_bits(dev->regmap, R14_AGC23, 0x1f, utmp2); ++ if (ret) ++ goto err; ++ ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ utmp = 98; ++ break; ++ default: /* DVB-C/QAM */ ++ utmp = 90; ++ break; ++ } ++ ret = regmap_write_bits(dev->regmap, R16_AGC25, 0xf8, utmp); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write_bits(dev->regmap, R12_AGC21, 0x60, ++ (c->frequency > 800000000) ? 0x40 : 0x20); ++ if (ret) ++ goto err; ++ ++ /* AGC3 */ ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ utmp = (c->frequency < 320000000) ? 5 : 7; ++ utmp2 = (c->frequency < 320000000) ? 10 : 12; ++ break; ++ default: /* DVB-C/QAM */ ++ utmp = 7; ++ utmp2 = 12; ++ break; ++ } ++ ret = regmap_write(dev->regmap, R17_AGC31, (utmp << 4) | utmp2); ++ if (ret) ++ goto err; ++ ++ /* S2D */ ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ if (c->bandwidth_hz == 8000000) ++ utmp = 0x04; ++ else ++ utmp = (c->frequency < 320000000) ? 0x04 : 0x02; ++ break; ++ default: /* DVB-C/QAM */ ++ if (c->bandwidth_hz == 6000000) ++ utmp = ((c->frequency > 172544000) && ++ (c->frequency < 320000000)) ? 0x04 : 0x02; ++ else /* 7.935 and 8 MHz */ ++ utmp = ((c->frequency > 320000000) && ++ (c->frequency < 600000000)) ? 0x02 : 0x04; ++ break; ++ } ++ ret = regmap_write_bits(dev->regmap, R20_IR_MIX, 0x06, utmp); ++ if (ret) ++ goto err; ++ ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ utmp = 0; ++ break; ++ default: /* DVB-C/QAM */ ++ utmp = (c->frequency < 600000000) ? 0 : 3; ++ break; ++ } ++ ret = regmap_write_bits(dev->regmap, R16_AGC25, 0x03, utmp); ++ if (ret) ++ goto err; ++ ++ utmp = 0x09; ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ if (c->bandwidth_hz == 8000000) ++ utmp = 0x0c; ++ break; ++ default: /* DVB-C/QAM */ ++ utmp = 0x0c; ++ break; ++ } ++ ret = regmap_write_bits(dev->regmap, R0F_AGC14, 0x3f, utmp); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_dbg(&client->dev, "failed=%d", ret); ++ return ret; ++} ++ ++static int tda18250_pll_calc(struct dvb_frontend *fe, u8 *rdiv, ++ u8 *ndiv, u8 *icp) ++{ ++ struct i2c_client *client = fe->tuner_priv; ++ struct tda18250_dev *dev = i2c_get_clientdata(client); ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ unsigned int uval, exp, lopd, scale; ++ unsigned long fvco; ++ ++ ret = regmap_read(dev->regmap, R34_MD1, &uval); ++ if (ret) ++ goto err; ++ ++ exp = (uval & 0x70) >> 4; ++ if (exp > 5) ++ exp = 0; ++ lopd = 1 << (exp - 1); ++ scale = uval & 0x0f; ++ fvco = lopd * scale * ((c->frequency / 1000) + dev->if_frequency); ++ ++ switch (dev->xtal_freq) { ++ case TDA18250_XTAL_FREQ_16MHZ: ++ *rdiv = 1; ++ *ndiv = 0; ++ *icp = (fvco < 6622000) ? 0x05 : 0x02; ++ break; ++ case TDA18250_XTAL_FREQ_24MHZ: ++ case TDA18250_XTAL_FREQ_25MHZ: ++ *rdiv = 3; ++ *ndiv = 1; ++ *icp = (fvco < 6622000) ? 0x05 : 0x02; ++ break; ++ case TDA18250_XTAL_FREQ_27MHZ: ++ if (fvco < 6643000) { ++ *rdiv = 2; ++ *ndiv = 0; ++ *icp = 0x05; ++ } else if (fvco < 6811000) { ++ *rdiv = 2; ++ *ndiv = 0; ++ *icp = 0x06; ++ } else { ++ *rdiv = 3; ++ *ndiv = 1; ++ *icp = 0x02; ++ } ++ break; ++ case TDA18250_XTAL_FREQ_30MHZ: ++ *rdiv = 2; ++ *ndiv = 0; ++ *icp = (fvco < 6811000) ? 0x05 : 0x02; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ dev_dbg(&client->dev, ++ "lopd=%d scale=%u fvco=%lu, rdiv=%d ndiv=%d icp=%d", ++ lopd, scale, fvco, *rdiv, *ndiv, *icp); ++ return 0; ++err: ++ return ret; ++} ++ ++static int tda18250_set_params(struct dvb_frontend *fe) ++{ ++ struct i2c_client *client = fe->tuner_priv; ++ struct tda18250_dev *dev = i2c_get_clientdata(client); ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u32 if_khz; ++ int ret; ++ unsigned int i, j; ++ u8 utmp; ++ u8 buf[3]; ++ ++ #define REG 0 ++ #define MASK 1 ++ #define DVBT_6 2 ++ #define DVBT_7 3 ++ #define DVBT_8 4 ++ #define DVBC_6 5 ++ #define DVBC_8 6 ++ #define ATSC 7 ++ ++ static const u8 delsys_params[][16] = { ++ [REG] = { 0x22, 0x23, 0x24, 0x21, 0x0d, 0x0c, 0x0f, 0x14, ++ 0x0e, 0x12, 0x58, 0x59, 0x1a, 0x19, 0x1e, 0x30 }, ++ [MASK] = { 0x77, 0xff, 0xff, 0x87, 0xf0, 0x78, 0x07, 0xe0, ++ 0x60, 0x0f, 0x60, 0x0f, 0x33, 0x30, 0x80, 0x06 }, ++ [DVBT_6] = { 0x51, 0x03, 0x83, 0x82, 0x40, 0x48, 0x01, 0xe0, ++ 0x60, 0x0f, 0x60, 0x05, 0x03, 0x10, 0x00, 0x04 }, ++ [DVBT_7] = { 0x52, 0x03, 0x85, 0x82, 0x40, 0x48, 0x01, 0xe0, ++ 0x60, 0x0f, 0x60, 0x05, 0x03, 0x10, 0x00, 0x04 }, ++ [DVBT_8] = { 0x53, 0x03, 0x87, 0x82, 0x40, 0x48, 0x06, 0xe0, ++ 0x60, 0x07, 0x60, 0x05, 0x03, 0x10, 0x00, 0x04 }, ++ [DVBC_6] = { 0x32, 0x05, 0x86, 0x82, 0x50, 0x00, 0x06, 0x60, ++ 0x40, 0x0e, 0x60, 0x05, 0x33, 0x10, 0x00, 0x04 }, ++ [DVBC_8] = { 0x53, 0x03, 0x88, 0x82, 0x50, 0x00, 0x06, 0x60, ++ 0x40, 0x0e, 0x60, 0x05, 0x33, 0x10, 0x00, 0x04 }, ++ [ATSC] = { 0x51, 0x03, 0x83, 0x82, 0x40, 0x48, 0x01, 0xe0, ++ 0x40, 0x0e, 0x60, 0x05, 0x03, 0x00, 0x80, 0x04 }, ++ }; ++ ++ dev_dbg(&client->dev, ++ "delivery_system=%d frequency=%u bandwidth_hz=%u", ++ c->delivery_system, c->frequency, c->bandwidth_hz); ++ ++ ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ j = ATSC; ++ if_khz = dev->if_atsc; ++ break; ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ if (c->bandwidth_hz == 0) { ++ ret = -EINVAL; ++ goto err; ++ } else if (c->bandwidth_hz <= 6000000) { ++ j = DVBT_6; ++ if_khz = dev->if_dvbt_6; ++ } else if (c->bandwidth_hz <= 7000000) { ++ j = DVBT_7; ++ if_khz = dev->if_dvbt_7; ++ } else if (c->bandwidth_hz <= 8000000) { ++ j = DVBT_8; ++ if_khz = dev->if_dvbt_8; ++ } else { ++ ret = -EINVAL; ++ goto err; ++ } ++ break; ++ case SYS_DVBC_ANNEX_A: ++ case SYS_DVBC_ANNEX_C: ++ if (c->bandwidth_hz == 0) { ++ ret = -EINVAL; ++ goto err; ++ } else if (c->bandwidth_hz <= 6000000) { ++ j = DVBC_6; ++ if_khz = dev->if_dvbc_6; ++ } else if (c->bandwidth_hz <= 8000000) { ++ j = DVBC_8; ++ if_khz = dev->if_dvbc_8; ++ } else { ++ ret = -EINVAL; ++ goto err; ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ dev_err(&client->dev, "unsupported delivery system=%d", ++ c->delivery_system); ++ goto err; ++ } ++ ++ /* set delivery system dependent registers */ ++ for (i = 0; i < 16; i++) { ++ ret = regmap_write_bits(dev->regmap, delsys_params[REG][i], ++ delsys_params[MASK][i], delsys_params[j][i]); ++ if (ret) ++ goto err; ++ } ++ ++ /* set IF if needed */ ++ if (dev->if_frequency != if_khz) { ++ utmp = DIV_ROUND_CLOSEST(if_khz, 50); ++ ret = regmap_write(dev->regmap, R26_IF, utmp); ++ if (ret) ++ goto err; ++ dev->if_frequency = if_khz; ++ dev_dbg(&client->dev, "set IF=%u kHz", if_khz); ++ ++ } ++ ++ ret = tda18250_set_agc(fe); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write_bits(dev->regmap, R1A_AGCK, 0x03, 0x01); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write_bits(dev->regmap, R14_AGC23, 0x40, 0x00); ++ if (ret) ++ goto err; ++ ++ /* set frequency */ ++ buf[0] = ((c->frequency / 1000) >> 16) & 0xff; ++ buf[1] = ((c->frequency / 1000) >> 8) & 0xff; ++ buf[2] = ((c->frequency / 1000) >> 0) & 0xff; ++ ret = regmap_bulk_write(dev->regmap, R27_RF1, buf, 3); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write(dev->regmap, R0A_IRQ3, TDA18250_IRQ_TUNE); ++ if (ret) ++ goto err; ++ ++ /* initial tune */ ++ ret = regmap_write(dev->regmap, R2A_MSM1, 0x01); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write(dev->regmap, R2B_MSM2, 0x01); ++ if (ret) ++ goto err; ++ ++ ret = tda18250_wait_for_irq(fe, 500, 10, TDA18250_IRQ_TUNE); ++ if (ret) ++ goto err; ++ ++ /* calc ndiv and rdiv */ ++ ret = tda18250_pll_calc(fe, &buf[0], &buf[1], &buf[2]); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write_bits(dev->regmap, R4F_XTALFLX3, 0xe0, ++ (buf[0] << 6) | (buf[1] << 5)); ++ if (ret) ++ goto err; ++ ++ /* clear IRQ */ ++ ret = regmap_write(dev->regmap, R0A_IRQ3, TDA18250_IRQ_TUNE); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write_bits(dev->regmap, R46_CPUMP, 0x07, 0x00); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write_bits(dev->regmap, R39_SD5, 0x03, 0x00); ++ if (ret) ++ goto err; ++ ++ /* tune again */ ++ ret = regmap_write(dev->regmap, R2A_MSM1, 0x01); /* tune */ ++ if (ret) ++ goto err; ++ ++ ret = regmap_write(dev->regmap, R2B_MSM2, 0x01); /* go */ ++ if (ret) ++ goto err; ++ ++ ret = tda18250_wait_for_irq(fe, 500, 10, TDA18250_IRQ_TUNE); ++ if (ret) ++ goto err; ++ ++ /* pll locking */ ++ msleep(20); ++ ++ ret = regmap_write_bits(dev->regmap, R2B_MSM2, 0x04, 0x04); ++ if (ret) ++ goto err; ++ ++ msleep(20); ++ ++ /* restore AGCK */ ++ ret = regmap_write_bits(dev->regmap, R1A_AGCK, 0x03, 0x03); ++ if (ret) ++ goto err; ++ ++ ret = regmap_write_bits(dev->regmap, R14_AGC23, 0x40, 0x40); ++ if (ret) ++ goto err; ++ ++ /* charge pump */ ++ ret = regmap_write_bits(dev->regmap, R46_CPUMP, 0x07, buf[2]); ++ ++ return 0; ++err: ++ return ret; ++} ++ ++static int tda18250_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct i2c_client *client = fe->tuner_priv; ++ struct tda18250_dev *dev = i2c_get_clientdata(client); ++ ++ *frequency = dev->if_frequency * 1000; ++ return 0; ++} ++ ++static int tda18250_sleep(struct dvb_frontend *fe) ++{ ++ struct i2c_client *client = fe->tuner_priv; ++ struct tda18250_dev *dev = i2c_get_clientdata(client); ++ int ret; ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ /* power down LNA */ ++ ret = regmap_write_bits(dev->regmap, R0C_AGC11, 0x80, 0x00); ++ if (ret) ++ return ret; ++ ++ /* set if freq to 0 in order to make sure it's set after wake up */ ++ dev->if_frequency = 0; ++ ++ ret = tda18250_power_control(fe, TDA18250_POWER_STANDBY); ++ return ret; ++} ++ ++static const struct dvb_tuner_ops tda18250_ops = { ++ .info = { ++ .name = "NXP TDA18250", ++ .frequency_min = 42000000, ++ .frequency_max = 870000000, ++ }, ++ ++ .init = tda18250_init, ++ .set_params = tda18250_set_params, ++ .get_if_frequency = tda18250_get_if_frequency, ++ .sleep = tda18250_sleep, ++}; ++ ++static int tda18250_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct tda18250_config *cfg = client->dev.platform_data; ++ struct dvb_frontend *fe = cfg->fe; ++ struct tda18250_dev *dev; ++ int ret; ++ unsigned char chip_id[3]; ++ ++ /* some registers are always read from HW */ ++ static const struct regmap_range tda18250_yes_ranges[] = { ++ regmap_reg_range(R05_POWER1, R0B_IRQ4), ++ regmap_reg_range(R21_IF_AGC, R21_IF_AGC), ++ regmap_reg_range(R2A_MSM1, R2B_MSM2), ++ regmap_reg_range(R2F_RSSI1, R31_IRQ_CTRL), ++ }; ++ ++ static const struct regmap_access_table tda18250_volatile_table = { ++ .yes_ranges = tda18250_yes_ranges, ++ .n_yes_ranges = ARRAY_SIZE(tda18250_yes_ranges), ++ }; ++ ++ static const struct regmap_config tda18250_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = TDA18250_NUM_REGS - 1, ++ .volatile_table = &tda18250_volatile_table, ++ }; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ i2c_set_clientdata(client, dev); ++ ++ dev->fe = cfg->fe; ++ dev->loopthrough = cfg->loopthrough; ++ if (cfg->xtal_freq < TDA18250_XTAL_FREQ_MAX) { ++ dev->xtal_freq = cfg->xtal_freq; ++ } else { ++ ret = -EINVAL; ++ dev_err(&client->dev, "xtal_freq invalid=%d", cfg->xtal_freq); ++ goto err_kfree; ++ } ++ dev->if_dvbt_6 = cfg->if_dvbt_6; ++ dev->if_dvbt_7 = cfg->if_dvbt_7; ++ dev->if_dvbt_8 = cfg->if_dvbt_8; ++ dev->if_dvbc_6 = cfg->if_dvbc_6; ++ dev->if_dvbc_8 = cfg->if_dvbc_8; ++ dev->if_atsc = cfg->if_atsc; ++ ++ dev->if_frequency = 0; ++ dev->warm = false; ++ ++ dev->regmap = devm_regmap_init_i2c(client, &tda18250_regmap_config); ++ if (IS_ERR(dev->regmap)) { ++ ret = PTR_ERR(dev->regmap); ++ goto err_kfree; ++ } ++ ++ /* read the three chip ID registers */ ++ regmap_bulk_read(dev->regmap, R00_ID1, &chip_id, 3); ++ dev_dbg(&client->dev, "chip_id=%02x:%02x:%02x", ++ chip_id[0], chip_id[1], chip_id[2]); ++ ++ switch (chip_id[0]) { ++ case 0xc7: ++ dev->slave = false; ++ break; ++ case 0x47: ++ dev->slave = true; ++ break; ++ default: ++ ret = -ENODEV; ++ goto err_kfree; ++ } ++ ++ if (chip_id[1] != 0x4a) { ++ ret = -ENODEV; ++ goto err_kfree; ++ } ++ ++ switch (chip_id[2]) { ++ case 0x20: ++ dev_info(&client->dev, ++ "NXP TDA18250AHN/%s successfully identified", ++ dev->slave ? "S" : "M"); ++ break; ++ case 0x21: ++ dev_info(&client->dev, ++ "NXP TDA18250BHN/%s successfully identified", ++ dev->slave ? "S" : "M"); ++ break; ++ default: ++ ret = -ENODEV; ++ goto err_kfree; ++ } ++ ++ fe->tuner_priv = client; ++ memcpy(&fe->ops.tuner_ops, &tda18250_ops, ++ sizeof(struct dvb_tuner_ops)); ++ ++ /* put the tuner in standby */ ++ tda18250_power_control(fe, TDA18250_POWER_STANDBY); ++ ++ return 0; ++err_kfree: ++ kfree(dev); ++err: ++ dev_dbg(&client->dev, "failed=%d", ret); ++ return ret; ++} ++ ++static int tda18250_remove(struct i2c_client *client) ++{ ++ struct tda18250_dev *dev = i2c_get_clientdata(client); ++ struct dvb_frontend *fe = dev->fe; ++ ++ dev_dbg(&client->dev, "\n"); ++ ++ memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); ++ fe->tuner_priv = NULL; ++ kfree(dev); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id tda18250_id_table[] = { ++ {"tda18250", 0}, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, tda18250_id_table); ++ ++static struct i2c_driver tda18250_driver = { ++ .driver = { ++ .name = "tda18250", ++ }, ++ .probe = tda18250_probe, ++ .remove = tda18250_remove, ++ .id_table = tda18250_id_table, ++}; ++ ++module_i2c_driver(tda18250_driver); ++ ++MODULE_DESCRIPTION("NXP TDA18250 silicon tuner driver"); ++MODULE_AUTHOR("Olli Salonen "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/tuners/tda18250.h b/drivers/media/tuners/tda18250.h +new file mode 100644 +index 0000000..fb56906 +--- /dev/null ++++ b/drivers/media/tuners/tda18250.h +@@ -0,0 +1,51 @@ ++/* ++ * NXP TDA18250BHN silicon tuner driver ++ * ++ * Copyright (C) 2017 Olli Salonen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef TDA18250_H ++#define TDA18250_H ++ ++#include ++#include ++#include "dvb_frontend.h" ++ ++#define TDA18250_XTAL_FREQ_16MHZ 0 ++#define TDA18250_XTAL_FREQ_24MHZ 1 ++#define TDA18250_XTAL_FREQ_25MHZ 2 ++#define TDA18250_XTAL_FREQ_27MHZ 3 ++#define TDA18250_XTAL_FREQ_30MHZ 4 ++#define TDA18250_XTAL_FREQ_MAX 5 ++ ++struct tda18250_config { ++ u16 if_dvbt_6; ++ u16 if_dvbt_7; ++ u16 if_dvbt_8; ++ u16 if_dvbc_6; ++ u16 if_dvbc_8; ++ u16 if_atsc; ++ u8 xtal_freq; ++ bool loopthrough; ++ ++ /* ++ * frontend ++ */ ++ struct dvb_frontend *fe; ++ ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ struct media_device *mdev; ++#endif ++}; ++ ++#endif +diff --git a/drivers/media/tuners/tda18250_priv.h b/drivers/media/tuners/tda18250_priv.h +new file mode 100644 +index 0000000..4a6f801 +--- /dev/null ++++ b/drivers/media/tuners/tda18250_priv.h +@@ -0,0 +1,145 @@ ++/* ++ * NXP TDA18250BHN silicon tuner driver ++ * ++ * Copyright (C) 2017 Olli Salonen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef TDA18250_PRIV_H ++#define TDA18250_PRIV_H ++ ++#include "tda18250.h" ++ ++#define R00_ID1 0x00 /* ID byte 1 */ ++#define R01_ID2 0x01 /* ID byte 2 */ ++#define R02_ID3 0x02 /* ID byte 3 */ ++#define R03_THERMO1 0x03 /* Thermo byte 1 */ ++#define R04_THERMO2 0x04 /* Thermo byte 2 */ ++#define R05_POWER1 0x05 /* Power byte 1 */ ++#define R06_POWER2 0x06 /* Power byte 2 */ ++#define R07_GPIO 0x07 /* GPIO */ ++#define R08_IRQ1 0x08 /* IRQ */ ++#define R09_IRQ2 0x09 /* IRQ */ ++#define R0A_IRQ3 0x0a /* IRQ */ ++#define R0B_IRQ4 0x0b /* IRQ */ ++#define R0C_AGC11 0x0c /* AGC1 byte 1 */ ++#define R0D_AGC12 0x0d /* AGC1 byte 2 */ ++#define R0E_AGC13 0x0e /* AGC1 byte 3 */ ++#define R0F_AGC14 0x0f /* AGC1 byte 4 */ ++#define R10_LT1 0x10 /* LT byte 1 */ ++#define R11_LT2 0x11 /* LT byte 2 */ ++#define R12_AGC21 0x12 /* AGC2 byte 1 */ ++#define R13_AGC22 0x13 /* AGC2 byte 2 */ ++#define R14_AGC23 0x14 /* AGC2 byte 3 */ ++#define R15_AGC24 0x15 /* AGC2 byte 4 */ ++#define R16_AGC25 0x16 /* AGC2 byte 5 */ ++#define R17_AGC31 0x17 /* AGC3 byte 1 */ ++#define R18_AGC32 0x18 /* AGC3 byte 2 */ ++#define R19_AGC33 0x19 /* AGC3 byte 3 */ ++#define R1A_AGCK 0x1a ++#define R1B_GAIN1 0x1b ++#define R1C_GAIN2 0x1c ++#define R1D_GAIN3 0x1d ++#define R1E_WI_FI 0x1e /* Wireless Filter */ ++#define R1F_RF_BPF 0x1f /* RF Band Pass Filter */ ++#define R20_IR_MIX 0x20 /* IR Mixer */ ++#define R21_IF_AGC 0x21 ++#define R22_IF1 0x22 /* IF byte 1 */ ++#define R23_IF2 0x23 /* IF byte 2 */ ++#define R24_IF3 0x24 /* IF byte 3 */ ++#define R25_REF 0x25 /* reference byte */ ++#define R26_IF 0x26 /* IF frequency */ ++#define R27_RF1 0x27 /* RF frequency byte 1 */ ++#define R28_RF2 0x28 /* RF frequency byte 2 */ ++#define R29_RF3 0x29 /* RF frequency byte 3 */ ++#define R2A_MSM1 0x2a ++#define R2B_MSM2 0x2b ++#define R2C_PS1 0x2c /* power saving mode byte 1 */ ++#define R2D_PS2 0x2d /* power saving mode byte 2 */ ++#define R2E_PS3 0x2e /* power saving mode byte 3 */ ++#define R2F_RSSI1 0x2f ++#define R30_RSSI2 0x30 ++#define R31_IRQ_CTRL 0x31 ++#define R32_DUMMY 0x32 ++#define R33_TEST 0x33 ++#define R34_MD1 0x34 ++#define R35_SD1 0x35 ++#define R36_SD2 0x36 ++#define R37_SD3 0x37 ++#define R38_SD4 0x38 ++#define R39_SD5 0x39 ++#define R3A_SD_TEST 0x3a ++#define R3B_REGU 0x3b ++#define R3C_RCCAL1 0x3c ++#define R3D_RCCAL2 0x3d ++#define R3E_IRCAL1 0x3e ++#define R3F_IRCAL2 0x3f ++#define R40_IRCAL3 0x40 ++#define R41_IRCAL4 0x41 ++#define R42_IRCAL5 0x42 ++#define R43_PD1 0x43 /* power down byte 1 */ ++#define R44_PD2 0x44 /* power down byte 2 */ ++#define R45_PD 0x45 /* power down */ ++#define R46_CPUMP 0x46 /* charge pump */ ++#define R47_LNAPOL 0x47 /* LNA polar casc */ ++#define R48_SMOOTH1 0x48 /* smooth test byte 1 */ ++#define R49_SMOOTH2 0x49 /* smooth test byte 2 */ ++#define R4A_SMOOTH3 0x4a /* smooth test byte 3 */ ++#define R4B_XTALOSC1 0x4b ++#define R4C_XTALOSC2 0x4c ++#define R4D_XTALFLX1 0x4d ++#define R4E_XTALFLX2 0x4e ++#define R4F_XTALFLX3 0x4f ++#define R50_XTALFLX4 0x50 ++#define R51_XTALFLX5 0x51 ++#define R52_IRLOOP0 0x52 ++#define R53_IRLOOP1 0x53 ++#define R54_IRLOOP2 0x54 ++#define R55_IRLOOP3 0x55 ++#define R56_IRLOOP4 0x56 ++#define R57_PLL_LOG 0x57 ++#define R58_AGC2_UP1 0x58 ++#define R59_AGC2_UP2 0x59 ++#define R5A_H3H5 0x5a ++#define R5B_AGC_AUTO 0x5b ++#define R5C_AGC_DEBUG 0x5c ++ ++#define TDA18250_NUM_REGS 93 ++ ++#define TDA18250_POWER_STANDBY 0 ++#define TDA18250_POWER_NORMAL 1 ++ ++#define TDA18250_IRQ_CAL 0x81 ++#define TDA18250_IRQ_HW_INIT 0x82 ++#define TDA18250_IRQ_TUNE 0x88 ++ ++struct tda18250_dev { ++ struct mutex i2c_mutex; ++ struct dvb_frontend *fe; ++ struct i2c_adapter *i2c; ++ struct regmap *regmap; ++ u8 xtal_freq; ++ /* IF in kHz */ ++ u16 if_dvbt_6; ++ u16 if_dvbt_7; ++ u16 if_dvbt_8; ++ u16 if_dvbc_6; ++ u16 if_dvbc_8; ++ u16 if_atsc; ++ u16 if_frequency; ++ bool slave; ++ bool loopthrough; ++ bool warm; ++ u8 regs[TDA18250_NUM_REGS]; ++}; ++ ++#endif +diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig +index f1a8276..1958d9e 100644 +--- a/drivers/media/usb/dvb-usb/Kconfig ++++ b/drivers/media/usb/dvb-usb/Kconfig +@@ -86,6 +86,7 @@ config DVB_USB_DIB0700 + select DVB_USB_DIB3000MC if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_MN88472 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUNER_DIB0090 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT +@@ -94,6 +95,7 @@ config DVB_USB_DIB0700 + select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA18250 if MEDIA_SUBDRV_AUTOSELECT + help + Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The + USB bridge is also present in devices having the DiB7700 DVB-T-USB +diff --git a/drivers/media/usb/dvb-usb/dib0700.h b/drivers/media/usb/dvb-usb/dib0700.h +index f89ab3b..3a9d4c2 100644 +--- a/drivers/media/usb/dvb-usb/dib0700.h ++++ b/drivers/media/usb/dvb-usb/dib0700.h +@@ -51,6 +51,8 @@ struct dib0700_state { + int (*read_status)(struct dvb_frontend *, enum fe_status *); + int (*sleep)(struct dvb_frontend* fe); + u8 buf[255]; ++ struct i2c_client *i2c_client_demod; ++ struct i2c_client *i2c_client_tuner; + }; + + extern int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, +diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c +index 1ee7ec5..94bd176 100644 +--- a/drivers/media/usb/dvb-usb/dib0700_core.c ++++ b/drivers/media/usb/dvb-usb/dib0700_core.c +@@ -911,10 +911,34 @@ static int dib0700_probe(struct usb_interface *intf, + return -ENODEV; + } + ++static void dib0700_disconnect(struct usb_interface *intf) ++{ ++ struct dvb_usb_device *d = usb_get_intfdata(intf); ++ struct dib0700_state *st = d->priv; ++ struct i2c_client *client; ++ ++ /* remove I2C client for tuner */ ++ client = st->i2c_client_tuner; ++ if (client) { ++ module_put(client->dev.driver->owner); ++ i2c_unregister_device(client); ++ } ++ ++ /* remove I2C client for demodulator */ ++ client = st->i2c_client_demod; ++ if (client) { ++ module_put(client->dev.driver->owner); ++ i2c_unregister_device(client); ++ } ++ ++ dvb_usb_device_exit(intf); ++} ++ ++ + static struct usb_driver dib0700_driver = { + .name = "dvb_usb_dib0700", + .probe = dib0700_probe, +- .disconnect = dvb_usb_device_exit, ++ .disconnect = dib0700_disconnect, + .id_table = dib0700_usb_id_table, + }; + +diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c +index 92098c1..8afcd11 100644 +--- a/drivers/media/usb/dvb-usb/dib0700_devices.c ++++ b/drivers/media/usb/dvb-usb/dib0700_devices.c +@@ -23,6 +23,9 @@ + #include "dib0090.h" + #include "lgdt3305.h" + #include "mxl5007t.h" ++#include "mn88472.h" ++#include "tda18250.h" ++ + + static int force_lna_activation; + module_param(force_lna_activation, int, 0644); +@@ -3725,6 +3728,90 @@ static int mxl5007t_tuner_attach(struct dvb_usb_adapter *adap) + &hcw_mxl5007t_config) == NULL ? -ENODEV : 0; + } + ++static int xbox_one_attach(struct dvb_usb_adapter *adap) ++{ ++ struct dib0700_state *st = adap->dev->priv; ++ struct i2c_client *client_demod, *client_tuner; ++ struct dvb_usb_device *d = adap->dev; ++ struct mn88472_config mn88472_config = { }; ++ struct tda18250_config tda18250_config; ++ struct i2c_board_info info; ++ ++ st->fw_use_new_i2c_api = 1; ++ st->disable_streaming_master_mode = 1; ++ ++ /* fe power enable */ ++ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); ++ msleep(30); ++ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); ++ msleep(30); ++ ++ /* demod reset */ ++ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); ++ msleep(30); ++ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); ++ msleep(30); ++ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); ++ msleep(30); ++ ++ /* attach demod */ ++ mn88472_config.fe = &adap->fe_adap[0].fe; ++ mn88472_config.i2c_wr_max = 22; ++ mn88472_config.xtal = 20500000; ++ mn88472_config.ts_mode = PARALLEL_TS_MODE; ++ mn88472_config.ts_clock = FIXED_TS_CLOCK; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "mn88472", I2C_NAME_SIZE); ++ info.addr = 0x18; ++ info.platform_data = &mn88472_config; ++ request_module(info.type); ++ client_demod = i2c_new_device(&d->i2c_adap, &info); ++ if (client_demod == NULL || client_demod->dev.driver == NULL) ++ goto fail_demod_device; ++ if (!try_module_get(client_demod->dev.driver->owner)) ++ goto fail_demod_module; ++ ++ st->i2c_client_demod = client_demod; ++ ++ adap->fe_adap[0].fe = mn88472_config.get_dvb_frontend(client_demod); ++ ++ /* attach tuner */ ++ memset(&tda18250_config, 0, sizeof(tda18250_config)); ++ tda18250_config.if_dvbt_6 = 3950; ++ tda18250_config.if_dvbt_7 = 4450; ++ tda18250_config.if_dvbt_8 = 4950; ++ tda18250_config.if_dvbc_6 = 4950; ++ tda18250_config.if_dvbc_8 = 4950; ++ tda18250_config.if_atsc = 4079; ++ tda18250_config.loopthrough = true; ++ tda18250_config.xtal_freq = TDA18250_XTAL_FREQ_27MHZ; ++ tda18250_config.fe = adap->fe_adap[0].fe; ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "tda18250", I2C_NAME_SIZE); ++ info.addr = 0x60; ++ info.platform_data = &tda18250_config; ++ ++ request_module(info.type); ++ client_tuner = i2c_new_device(&adap->dev->i2c_adap, &info); ++ if (client_tuner == NULL || client_tuner->dev.driver == NULL) ++ goto fail_tuner_device; ++ if (!try_module_get(client_tuner->dev.driver->owner)) ++ goto fail_tuner_module; ++ ++ st->i2c_client_tuner = client_tuner; ++ return 0; ++ ++fail_tuner_module: ++ i2c_unregister_device(client_tuner); ++fail_tuner_device: ++ module_put(client_demod->dev.driver->owner); ++fail_demod_module: ++ i2c_unregister_device(client_demod); ++fail_demod_device: ++ return -ENODEV; ++} ++ + + /* DVB-USB and USB stuff follows */ + struct usb_device_id dib0700_usb_id_table[] = { +@@ -3816,7 +3903,8 @@ struct usb_device_id dib0700_usb_id_table[] = { + { USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_2002E_SE) }, + { USB_DEVICE(USB_VID_PCTV, USB_PID_DIBCOM_STK8096PVR) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK8096PVR) }, +- { USB_DEVICE(USB_VID_HAMA, USB_PID_HAMA_DVBT_HYBRID) }, ++/* 85 */{ USB_DEVICE(USB_VID_HAMA, USB_PID_HAMA_DVBT_HYBRID) }, ++ { USB_DEVICE(USB_VID_MICROSOFT, USB_PID_XBOX_ONE_TUNER) }, + { 0 } /* Terminating entry */ + }; + MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); +@@ -5040,6 +5128,25 @@ struct dvb_usb_device_properties dib0700_devices[] = { + RC_PROTO_BIT_NEC, + .change_protocol = dib0700_change_protocol, + }, ++ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, ++ .num_adapters = 1, ++ .adapter = { ++ { ++ DIB0700_NUM_FRONTENDS(1), ++ .fe = {{ ++ .frontend_attach = xbox_one_attach, ++ ++ DIB0700_DEFAULT_STREAMING_CONFIG(0x82), ++ } }, ++ }, ++ }, ++ .num_device_descs = 1, ++ .devices = { ++ { "Microsoft Xbox One Digital TV Tuner", ++ { &dib0700_usb_id_table[86], NULL }, ++ { NULL }, ++ }, ++ }, + }, + }; + +-- +2.14.1 + From 8b4322df6aa853b862369c8b51a048944df083b1 Mon Sep 17 00:00:00 2001 From: cvh Date: Sun, 10 Dec 2017 18:50:37 +0100 Subject: [PATCH 10/11] driver addons: enable for RPi --- projects/RPi/options | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/RPi/options b/projects/RPi/options index 940d7cc660..a6d4792431 100644 --- a/projects/RPi/options +++ b/projects/RPi/options @@ -167,9 +167,9 @@ fi fi # build and install driver addons (yes / no) - DRIVER_ADDONS_SUPPORT="no" + DRIVER_ADDONS_SUPPORT="yes" # driver addons to install: # for a list of additinoal drivers see packages/linux-driver-addons # Space separated list is supported, - DRIVER_ADDONS="" + DRIVER_ADDONS="crazycat hauppauge" From 5f3b67d2398ee9b3486ef3df50322d526e96d443 Mon Sep 17 00:00:00 2001 From: cvh Date: Sun, 10 Dec 2017 18:50:38 +0100 Subject: [PATCH 11/11] driver addons: enable for Generic --- projects/Generic/options | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/Generic/options b/projects/Generic/options index 41e6871c9e..56da7426e2 100644 --- a/projects/Generic/options +++ b/projects/Generic/options @@ -90,9 +90,9 @@ ADDITIONAL_DRIVERS="$ADDITIONAL_DRIVERS bcm_sta intel_nuc_led" # build and install driver addons (yes / no) - DRIVER_ADDONS_SUPPORT="no" + DRIVER_ADDONS_SUPPORT="yes" # driver addons to install: # for a list of additinoal drivers see packages/linux-driver-addons # Space separated list is supported, - DRIVER_ADDONS="" + DRIVER_ADDONS="crazycat digital_devices hauppauge"