]> git.webhop.me Git - bs-cst-neutrino-hd.git/commitdiff
hd51/bre2ze4k/h7: re-add linux kernel patches
authorMarkham <markham001@gmx.de>
Wed, 1 Jan 2020 00:15:40 +0000 (01:15 +0100)
committerMarkham <markham001@gmx.de>
Wed, 1 Jan 2020 00:15:40 +0000 (01:15 +0100)
archive-patches/armbox/4_10_0001-export_pmpoweroffprepare.patch [new file with mode: 0644]
archive-patches/armbox/4_10_0002-TBS-fixes-for-4.10-kernel.patch [new file with mode: 0644]
archive-patches/armbox/4_10_0003-Support-TBS-USB-drivers-for-4.6-kernel.patch [new file with mode: 0644]
archive-patches/armbox/4_10_0004-TBS-fixes-for-4.6-kernel.patch [new file with mode: 0644]
archive-patches/armbox/4_10_0005-STV-Add-PLS-support.patch [new file with mode: 0644]
archive-patches/armbox/4_10_0006-STV-Add-SNR-Signal-report-parameters.patch [new file with mode: 0644]
archive-patches/armbox/4_10_0007-blindscan2.patch [new file with mode: 0644]
archive-patches/armbox/4_10_0007-stv090x-optimized-TS-sync-control.patch [new file with mode: 0644]
archive-patches/armbox/4_10_blacklist_mmc0.patch [new file with mode: 0644]
archive-patches/armbox/4_10_reserve_dvb_adapter_0.patch [new file with mode: 0644]
archive-patches/armbox/4_10_t230c2.patch [new file with mode: 0644]

diff --git a/archive-patches/armbox/4_10_0001-export_pmpoweroffprepare.patch b/archive-patches/armbox/4_10_0001-export_pmpoweroffprepare.patch
new file mode 100644 (file)
index 0000000..f832e8f
--- /dev/null
@@ -0,0 +1,12 @@
+diff --git a/kernel/reboot.c b/kernel/reboot.c
+index bd30a97..a6903bf 100644
+--- a/kernel/reboot.c
++++ b/kernel/reboot.c
+@@ -49,6 +49,7 @@
+  */
+ void (*pm_power_off_prepare)(void);
++EXPORT_SYMBOL(pm_power_off_prepare);
+ /**
+  *    emergency_restart - reboot the system
diff --git a/archive-patches/armbox/4_10_0002-TBS-fixes-for-4.10-kernel.patch b/archive-patches/armbox/4_10_0002-TBS-fixes-for-4.10-kernel.patch
new file mode 100644 (file)
index 0000000..d96d50a
--- /dev/null
@@ -0,0 +1,46 @@
+diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
+index 779f4224b63e..ae6005436639 100644
+--- a/drivers/media/dvb-core/dvb-usb-ids.h
++++ b/drivers/media/dvb-core/dvb-usb-ids.h
+@@ -411,6 +411,6 @@
+ #define USB_PID_PCTV_2002E_SE                           0x025d
+ #define USB_PID_SVEON_STV27                             0xd3af
+ #define USB_PID_TURBOX_DTT_2000                         0xd3a4
+-#define USB_PID_WINTV_SOLOHD                            0x0264
+ #define USB_PID_EVOLVEO_XTRATV_STICK                   0xa115
++#define USB_PID_WINTV_SOLOHD                            0x0264
+ #endif
+diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
+index 7ef469c0c866..b586d2a49737 100644
+--- a/drivers/media/dvb-frontends/stv090x.c
++++ b/drivers/media/dvb-frontends/stv090x.c
+@@ -3692,12 +3692,9 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
+                       }
+                       val /= 16;
+                       last = ARRAY_SIZE(stv090x_s2cn_tab) - 1;
+-                      div = stv090x_s2cn_tab[last].real -
+-                            stv090x_s2cn_tab[3].real;
+-                      val = stv090x_table_lookup(stv090x_s2cn_tab, last, val);
+-                      if (val < 0)
+-                              val = 0;
+-                      *cnr = val * 0xFFFF / div;
++                      div = stv090x_s2cn_tab[0].read -
++                            stv090x_s2cn_tab[last].read;
++                      *cnr = 0xFFFF - ((val * 0xFFFF) / div);
+               }
+               break;
+@@ -3717,10 +3714,9 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
+                       }
+                       val /= 16;
+                       last = ARRAY_SIZE(stv090x_s1cn_tab) - 1;
+-                      div = stv090x_s1cn_tab[last].real -
+-                            stv090x_s1cn_tab[0].real;
+-                      val = stv090x_table_lookup(stv090x_s1cn_tab, last, val);
+-                      *cnr = val * 0xFFFF / div;
++                      div = stv090x_s1cn_tab[0].read -
++                            stv090x_s1cn_tab[last].read;
++                      *cnr = 0xFFFF - ((val * 0xFFFF) / div);
+               }
+               break;
+       default:
diff --git a/archive-patches/armbox/4_10_0003-Support-TBS-USB-drivers-for-4.6-kernel.patch b/archive-patches/armbox/4_10_0003-Support-TBS-USB-drivers-for-4.6-kernel.patch
new file mode 100644 (file)
index 0000000..b7acd60
--- /dev/null
@@ -0,0 +1,1334 @@
+From 6f6cb195b8f7dbc2d0b4e31a2da4cd58bd69f6bf Mon Sep 17 00:00:00 2001
+From: Athanasios Oikonomou <athoik@gmail.com>
+Date: Sat, 5 Mar 2016 00:29:45 +0200
+Subject: [PATCH] Support TBS USB drivers
+
+This patch add supports for TBS USB drivers based on the following patches:
+
+https://patchwork.linuxtv.org/patch/23244/
+https://patchwork.linuxtv.org/patch/23243/
+https://patchwork.linuxtv.org/patch/23242/
+
+diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
+index c117fb3..5e0735b 100644
+--- a/drivers/media/dvb-core/dvb-usb-ids.h
++++ b/drivers/media/dvb-core/dvb-usb-ids.h
+@@ -78,6 +78,7 @@
+ #define USB_VID_EVOLUTEPC                     0x1e59
+ #define USB_VID_AZUREWAVE                     0x13d3
+ #define USB_VID_TECHNISAT                     0x14f7
++#define USB_VID_TENOW                         0x734c
+ /* Product IDs */
+ #define USB_PID_ADSTECH_USB2_COLD                     0xa333
+@@ -413,4 +414,10 @@
+ #define USB_PID_TURBOX_DTT_2000                         0xd3a4
+ #define USB_PID_EVOLVEO_XTRATV_STICK                   0xa115
+ #define USB_PID_WINTV_SOLOHD                            0x0264
++#define USB_PID_TENOW_TBS5910                         0x5910
++#define USB_PID_TENOW_TBS5920                         0x5920
++#define USB_PID_TENOW_TBS5921                         0x5921
++#define USB_PID_TENOW_TBS5925                         0x5925
++#define USB_PID_TENOW_TBS5928                         0x5928
++#define USB_PID_TENOW_TBS5980                         0x5980
+ #endif
+diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c
+index 8814f36..5ccfeb3 100644
+--- a/drivers/media/dvb-frontends/cx24116.c
++++ b/drivers/media/dvb-frontends/cx24116.c
+@@ -705,6 +705,9 @@ static int cx24116_read_status(struct dvb_frontend *fe, enum fe_status *status)
+       if (lock & CX24116_HAS_SYNCLOCK)
+               *status |= FE_HAS_SYNC | FE_HAS_LOCK;
++      if (state->config->set_lock_led)
++              state->config->set_lock_led(fe, *status & FE_HAS_LOCK);
++
+       return 0;
+ }
+@@ -1113,6 +1116,10 @@ static void cx24116_release(struct dvb_frontend *fe)
+ {
+       struct cx24116_state *state = fe->demodulator_priv;
+       dprintk("%s\n", __func__);
++
++      if (state->config->set_lock_led)
++              state->config->set_lock_led(fe, 0);
++
+       kfree(state);
+ }
+@@ -1198,6 +1205,9 @@ static int cx24116_sleep(struct dvb_frontend *fe)
+       dprintk("%s()\n", __func__);
++      if (state->config->set_lock_led)
++              state->config->set_lock_led(fe, 0);
++
+       /* Firmware CMD 36: Power config */
+       cmd.args[0x00] = CMD_TUNERSLEEP;
+       cmd.args[0x01] = 1;
+diff --git a/drivers/media/dvb-frontends/cx24116.h b/drivers/media/dvb-frontends/cx24116.h
+index f6dbabc..7cbb907 100644
+--- a/drivers/media/dvb-frontends/cx24116.h
++++ b/drivers/media/dvb-frontends/cx24116.h
+@@ -38,6 +38,9 @@ struct cx24116_config {
+       /* max bytes I2C provider can write at once */
+       u16 i2c_wr_max;
++
++      /* Hook for Lock LED */
++      void (*set_lock_led)(struct dvb_frontend *fe, int offon);
+ };
+ #if IS_REACHABLE(CONFIG_DVB_CX24116)
+diff --git a/drivers/media/dvb-frontends/stv0288.c b/drivers/media/dvb-frontends/stv0288.c
+index c93d9a4..9dcd922 100644
+--- a/drivers/media/dvb-frontends/stv0288.c
++++ b/drivers/media/dvb-frontends/stv0288.c
+@@ -382,6 +382,9 @@ static int stv0288_read_status(struct dvb_frontend *fe, enum fe_status *status)
+               dprintk("stv0288 has locked\n");
+       }
++      if (state->config->set_lock_led)
++              state->config->set_lock_led(fe, *status & FE_HAS_LOCK);
++
+       return 0;
+ }
+@@ -416,6 +419,9 @@ static int stv0288_sleep(struct dvb_frontend *fe)
+ {
+       struct stv0288_state *state = fe->demodulator_priv;
++      if (state->config->set_lock_led)
++              state->config->set_lock_led(fe, 0);
++
+       stv0288_writeregI(state, 0x41, 0x84);
+       state->initialised = 0;
+@@ -532,6 +538,10 @@ static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+ static void stv0288_release(struct dvb_frontend *fe)
+ {
+       struct stv0288_state *state = fe->demodulator_priv;
++
++      if (state->config->set_lock_led)
++              state->config->set_lock_led(fe, 0);
++
+       kfree(state);
+ }
+diff --git a/drivers/media/dvb-frontends/stv0288.h b/drivers/media/dvb-frontends/stv0288.h
+index b58603c..01d8481 100644
+--- a/drivers/media/dvb-frontends/stv0288.h
++++ b/drivers/media/dvb-frontends/stv0288.h
+@@ -40,6 +40,9 @@ struct stv0288_config {
+       int min_delay_ms;
+       int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
++
++      /* Hook for Lock LED */
++      void (*set_lock_led)(struct dvb_frontend *fe, int offon);
+ };
+ #if IS_REACHABLE(CONFIG_DVB_STV0288)
+diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
+index 25bdf6e..ce99b9d 100644
+--- a/drivers/media/dvb-frontends/stv090x.c
++++ b/drivers/media/dvb-frontends/stv090x.c
+@@ -3553,6 +3553,9 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
+               break;
+       }
++      if (state->config->set_lock_led)
++              state->config->set_lock_led(fe, *status & FE_HAS_LOCK);
++
+       return 0;
+ }
+@@ -3901,6 +3904,9 @@ static int stv090x_sleep(struct dvb_frontend *fe)
+       u32 reg;
+       u8 full_standby = 0;
++      if (state->config->set_lock_led)
++              state->config->set_lock_led(fe, 0);
++
+       if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+               goto err;
+@@ -4132,6 +4138,9 @@ static void stv090x_release(struct dvb_frontend *fe)
+ {
+       struct stv090x_state *state = fe->demodulator_priv;
++      if (state->config->set_lock_led)
++              state->config->set_lock_led(fe, 0);
++
+       state->internal->num_used--;
+       if (state->internal->num_used <= 0) {
+diff --git a/drivers/media/dvb-frontends/stv090x.h b/drivers/media/dvb-frontends/stv090x.h
+index 012e55e..545f43a 100644
+--- a/drivers/media/dvb-frontends/stv090x.h
++++ b/drivers/media/dvb-frontends/stv090x.h
+@@ -89,6 +89,8 @@ struct stv090x_config {
+       bool diseqc_envelope_mode;
++      /* Hook for Lock LED */
++      void (*set_lock_led) (struct dvb_frontend *fe, int offon);
+       int (*tuner_init)(struct dvb_frontend *fe);
+       int (*tuner_sleep)(struct dvb_frontend *fe);
+       int (*tuner_set_mode)(struct dvb_frontend *fe, enum tuner_mode mode);
+diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h
+index 8f18402..891e007 100644
+--- a/drivers/media/dvb-frontends/tda10071.h
++++ b/drivers/media/dvb-frontends/tda10071.h
+@@ -49,6 +49,9 @@ struct tda10071_platform_data {
+       u8 pll_multiplier;
+       u8 tuner_i2c_addr;
++      /* Hook for Lock LED */
++      void (*set_lock_led)(struct dvb_frontend *fe, int offon);
++
+       struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
+ };
+diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig
+index 128eee6..49c34f9 100644
+--- a/drivers/media/usb/dvb-usb/Kconfig
++++ b/drivers/media/usb/dvb-usb/Kconfig
+@@ -331,3 +331,23 @@ config DVB_USB_TECHNISAT_USB2
+       select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
+       help
+         Say Y here to support the Technisat USB2 DVB-S/S2 device
++
++config DVB_USB_TBS
++      tristate "TurboSight DVB-S/S2 USB2.0 support"
++      depends on DVB_USB
++      select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
++      select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
++      select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
++      select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
++      select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT
++      select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
++      select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
++      select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
++      help
++        Say Y here to support TurboSight (TBS) DVB-S/S2 USB2.0 receivers.
++        Required firmware can be found at http://www.tbsdtv.com/download/
++        The tda10071 (TBS5921) firmware can be downloaded by executing:
++        Documentation/dvb/get_dvb_firmware tda10071
++
++        Supported devices are:
++        TBS5980 TBS5928 TBS5925 TBS5921 TBS5920 TBS5910
+diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile
+index acdd1ef..cb00137 100644
+--- a/drivers/media/usb/dvb-usb/Makefile
++++ b/drivers/media/usb/dvb-usb/Makefile
+@@ -79,6 +79,9 @@ obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o
+ dvb-usb-technisat-usb2-objs := technisat-usb2.o
+ obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o
++dvb-usb-tbsusb-objs := tbs-usb.o
++obj-$(CONFIG_DVB_USB_TBS) += dvb-usb-tbsusb.o
++
+ ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ ccflags-y += -I$(srctree)/drivers/media/dvb-frontends/
+ # due to tuner-xc3028
+diff --git a/drivers/media/usb/dvb-usb/tbs-usb.c b/drivers/media/usb/dvb-usb/tbs-usb.c
+new file mode 100644
+index 0000000..f142be3
+--- /dev/null
++++ b/drivers/media/usb/dvb-usb/tbs-usb.c
+@@ -0,0 +1,1075 @@
++/*
++ * TBS 5980/5928/5925/5921/5920/5910 DVB-S/S2 driver
++ *
++ * Copyright (c) 2008 Bob Liu (Bob@Turbosight.com)
++ *                    Igor M. Liplianin (liplianin@me.by)
++ * Copyright (c) 2009 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
++ * Copyright (c) 2014 Andreas Steinmetz <ast@domdv.de>
++ *                    Lock LED and TBS5921 stuff shamelessly taken from
++ *                    CrazyCat's Bitbucket repository
++ *                    TBS5925 Open Source version shamelessly taken from
++ *                    UpdateLee's Bitbucket repository
++ *
++ * 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, version 2.
++ *
++ */
++
++#include <linux/version.h>
++#include "tbs-usb.h"
++
++#include "stv6110x.h"
++#include "stv090x.h"
++#include "stb6100.h"
++#include "stb6100_cfg.h"
++
++#include "cx24116.h"
++
++#include "stv0299.h"
++#include "stv0288.h"
++#include "stb6000.h"
++
++#include "tda10071.h"
++
++#include "dvb_ca_en50221.h"
++
++struct tbsusbci_state {
++      u8 buf[20];
++      struct dvb_ca_en50221 ca;
++      struct mutex ca_mutex;
++};
++
++struct tbsusb_state {
++      u8 buf[20];
++};
++
++static int dvb_usb_tbsusb_debug;
++module_param_named(debug, dvb_usb_tbsusb_debug, int, 0644);
++MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer (or-able))."
++                                                      DVB_USB_DEBUG_STATUS);
++
++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
++
++static struct rc_map_table tbsusb_rc_keys[] = {
++      { 0xff84, KEY_POWER2},          /* power */
++      { 0xff94, KEY_MUTE},            /* mute */
++      { 0xff87, KEY_1},
++      { 0xff86, KEY_2},
++      { 0xff85, KEY_3},
++      { 0xff8b, KEY_4},
++      { 0xff8a, KEY_5},
++      { 0xff89, KEY_6},
++      { 0xff8f, KEY_7},
++      { 0xff8e, KEY_8},
++      { 0xff8d, KEY_9},
++      { 0xff92, KEY_0},
++      { 0xff96, KEY_CHANNELUP},       /* ch+ */
++      { 0xff91, KEY_CHANNELDOWN},     /* ch- */
++      { 0xff93, KEY_VOLUMEUP},        /* vol+ */
++      { 0xff8c, KEY_VOLUMEDOWN},      /* vol- */
++      { 0xff83, KEY_RECORD},          /* rec */
++      { 0xff98, KEY_PAUSE},           /* pause, yellow */
++      { 0xff99, KEY_OK},              /* ok */
++      { 0xff9a, KEY_CAMERA},          /* snapshot */
++      { 0xff81, KEY_UP},
++      { 0xff90, KEY_LEFT},
++      { 0xff82, KEY_RIGHT},
++      { 0xff88, KEY_DOWN},
++      { 0xff95, KEY_FAVORITES},       /* blue */
++      { 0xff97, KEY_SUBTITLE},        /* green */
++      { 0xff9d, KEY_ZOOM},
++      { 0xff9f, KEY_EXIT},
++      { 0xff9e, KEY_MENU},
++      { 0xff9c, KEY_EPG},
++      { 0xff80, KEY_PREVIOUS},        /* red */
++      { 0xff9b, KEY_MODE},
++      { 0xffdd, KEY_TV },
++      { 0xffde, KEY_PLAY },
++      { 0xffdc, KEY_STOP },
++      { 0xffdb, KEY_REWIND },
++      { 0xffda, KEY_FASTFORWARD },
++      { 0xffd9, KEY_PREVIOUS },       /* replay */
++      { 0xffd8, KEY_NEXT },           /* skip */
++      { 0xffd1, KEY_NUMERIC_STAR },
++      { 0xffd2, KEY_NUMERIC_POUND },
++      { 0xffd4, KEY_DELETE },         /* clear */
++};
++
++static int tbsusb_op_r_unlocked(struct usb_device *dev, int num, u8 request,
++                                      u16 value, u16 index, u8 *data, int len)
++{
++      int ret;
++
++      ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
++              USB_TYPE_VENDOR | USB_DIR_IN, value, index, data, len, 2000);
++
++      /* oh well, rc poll returns -EOVERFLOW but also the required data */
++      if (ret == -EOVERFLOW && request == 0xb8)
++              ret = 0;
++      else if (ret < 0) {
++              warn("usb read 0x%02x/%d from adapter %d failed. (%d)",
++                                                      request, len, num, ret);
++              ret = -EIO;
++      } else
++              ret = 0;
++
++      deb_xfer("read: adap.: %d, req. %02x, val: %04x, ind: %04x, buffer: ",
++              num, request, value, index);
++      debug_dump(data, len, deb_xfer);
++
++      return ret;
++}
++
++static int tbsusb_op_w_unlocked(struct usb_device *dev, int num, u8 request,
++                                      u16 value, u16 index, u8 *data, int len)
++{
++      int ret;
++
++      deb_xfer("write: adap.: %d, req. %02x, val: %04x, ind: %04x, buffer: ",
++              num, request, value, index);
++      debug_dump(data, len, deb_xfer);
++
++      ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
++              USB_TYPE_VENDOR | USB_DIR_OUT, value, index, data, len, 2000);
++
++      if (ret != len) {
++              warn("usb write 0x%02x/%d to adapter %d failed. (%d)",
++                                                      request, len, num, ret);
++              ret = -EIO;
++      } else
++              ret = 0;
++
++      return ret;
++}
++
++static int tbsusb_load_firmware(struct usb_device *dev,
++                      const struct firmware *frmwr)
++{
++      u8 *b;
++      int ret, i, j;
++
++      if (!dev || !frmwr)
++              return -ENODEV;
++
++      b = kmalloc(0x40, GFP_KERNEL);
++      if (!b)
++              return -ENOMEM;
++
++      /*stop the CPU*/
++      b[0] = 1;
++      ret = tbsusb_op_w_unlocked(dev, -1, 0xa0, 0x7f92, 0, b, 1);
++      if (!ret)
++              ret = tbsusb_op_w_unlocked(dev, -1, 0xa0, 0xe600, 0, b, 1);
++      if (ret) {
++              err("could not stop the USB controller CPU.");
++              goto err;
++      }
++
++      for (i = 0; i < frmwr->size; i += 0x40) {
++              j = i & 0x3f;
++              if (!j)
++                      j = 0x40;
++              memcpy(b, (u8 *) frmwr->data + i, j);
++              ret = tbsusb_op_w_unlocked(dev, -1, 0xa0, i, 0, b, j);
++              if (ret) {
++                      err("error while transferring firmware.");
++                      goto err;
++              }
++      }
++
++      /* restart the CPU */
++      b[0] = 0;
++      ret = tbsusb_op_w_unlocked(dev, -1, 0xa0, 0x7f92, 0, b, 1);
++      if (!ret)
++              ret = tbsusb_op_w_unlocked(dev, -1, 0xa0, 0xe600, 0, b, 1);
++      if (ret) {
++              err("could not restart the USB controller CPU.");
++              goto err;
++      }
++
++      msleep(100);
++
++err:  kfree(b);
++      return ret;
++}
++
++/* without copying the data back and forth usb transfers randomly fail... */
++
++static int tbsusb_op_wwr(struct dvb_usb_device *d, u8 wrq, u8 wrrq, int wwait,
++                              int wrwait, u8 *wdata, int wlen, u8 *wrdata,
++                              int wrlen, int wrop)
++{
++      struct tbsusb_state *s;
++      u8 *b;
++      int ret, len, num;
++
++      if (d && d->udev && d->priv)
++              s = (struct tbsusb_state *)d->priv;
++      else
++              return -ENODEV;
++
++      if (wrlen > wlen)
++              len = wrlen;
++      else
++              len = wlen;
++
++      if (len > sizeof(s->buf)) {
++              b = kmalloc(len, GFP_KERNEL);
++              if (!b)
++                      return -ENOMEM;
++      } else
++              b = s->buf;
++
++      if (d->adapter[0].fe_adap[0].fe && d->adapter[0].fe_adap[0].fe->dvb)
++              num = d->adapter[0].fe_adap[0].fe->dvb->num;
++      else
++              num = -1;
++
++      mutex_lock(&d->usb_mutex);
++      if (wdata) {
++              memcpy(b, wdata, wlen);
++              ret = tbsusb_op_w_unlocked(d->udev, num, wrq, 0, 0, b, wlen);
++              if (ret)
++                      goto err;
++              if (wwait)
++                      usleep_range(wwait, wwait+1000);
++      }
++      if (wrdata) {
++              if (wrop) {
++                      memcpy(b, wrdata, wrlen);
++                      ret = tbsusb_op_w_unlocked(d->udev, num, wrrq, 0, 0, b,
++                                                                      wrlen);
++              } else
++                      ret = tbsusb_op_r_unlocked(d->udev, num, wrrq, 0, 0, b,
++                                                                      wrlen);
++              if (ret)
++                      goto err;
++              if (!wrop)
++                      memcpy(wrdata, b, wrlen);
++              if (wrwait)
++                      usleep_range(wrwait, wrwait+1000);
++      }
++err:  mutex_unlock(&d->usb_mutex);
++
++      if (len > sizeof(s->buf))
++              kfree(b);
++
++      return ret;
++}
++
++static int tbsusb_op_ww(struct dvb_usb_device *d, u8 wrq1, u8 wrq2, int wwait1,
++                              int wwait2, u8 *wdata1, int wlen1, u8 *wdata2,
++                              int wlen2)
++{
++      return tbsusb_op_wwr(d, wrq1, wrq2, wwait1, wwait2, wdata1, wlen1,
++                                                      wdata2, wlen2, 1);
++}
++
++static int tbsusb_op_wr(struct dvb_usb_device *d, u8 wrq, u8 rrq, int wwait,
++                              int rwait, u8 *wdata, int wlen, u8 *rdata,
++                              int rlen)
++{
++      return tbsusb_op_wwr(d, wrq, rrq, wwait, rwait, wdata, wlen, rdata,
++                                                              rlen, 0);
++}
++
++static int tbsusb_op_w(struct dvb_usb_device *d, u8 req, int uwait,
++                                                      u8 *data, int len)
++{
++      return tbsusb_op_wwr(d, req, 0x00, uwait, 0, data, len, NULL, 0, 0);
++}
++
++static int tbsusb_op_r(struct dvb_usb_device *d, u8 req, int uwait,
++                                                      u8 *data, int len)
++{
++      return tbsusb_op_wwr(d, 0x00, req, 0, uwait, NULL, 0, data, len, 0);
++}
++
++static int tbsusb_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
++{
++      int i, ret;
++      u8 buf[3];
++
++      for (i = 0; i < 6; i++) {
++              buf[0] = 1;             /* length */
++              buf[1] = 0xa0;          /* eeprom addr */
++              buf[2] = i + 16;        /* register (0-255) */
++              ret = tbsusb_op_wr(d, 0x90, 0x91, 0, 0, buf, 3, &mac[i], 1);
++              if (ret) {
++                      err("eeprom read failed.");
++                      return ret;
++              }
++      }
++      return 0;
++};
++
++static int tbsusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
++{
++      int i;
++      u8 buf[4];
++
++      *state = REMOTE_NO_KEY_PRESSED;
++
++      i = tbsusb_op_r(d, 0xb8, 3000, buf, 4);
++      if (i)
++              goto out;
++
++      for (i = 0; i < ARRAY_SIZE(tbsusb_rc_keys); i++) {
++              if (rc5_data(&tbsusb_rc_keys[i]) == buf[3]) {
++                      *state = REMOTE_KEY_PRESSED;
++                      *event = tbsusb_rc_keys[i].keycode;
++                      break;
++              }
++      }
++
++out:  return 0;
++}
++
++static int tbsusb_set_pin(struct dvb_frontend *fe, u8 *what)
++{
++      struct dvb_usb_device *d = NULL;
++
++      if (fe && fe->dvb && fe->dvb->priv)
++              d = ((struct dvb_usb_adapter *)fe->dvb->priv)->dev;
++      if (!d)
++              return -ENODEV;
++
++      return tbsusb_op_w(d, 0x8a, 0, what, 2);
++}
++
++static int tbsusb_set_voltage(struct dvb_frontend *fe,
++                                              fe_sec_voltage_t voltage)
++{
++      static u8 command_13v[2] = {0x03, 0x00};
++      static u8 command_18v[2] = {0x03, 0x01};
++
++      return tbsusb_set_pin(fe,
++                      voltage == SEC_VOLTAGE_18 ? command_18v : command_13v);
++}
++
++static void tbsusb_led_ctrl(struct dvb_frontend *fe, int onoff)
++{
++      static u8 led_off[2] = {0x05, 0x00};
++      static u8 led_on[2]  = {0x05, 0x01};
++
++      tbsusb_set_pin(fe, onoff ? led_on : led_off);
++}
++
++static int tbsusb_i2c_transfer(struct i2c_adapter *adap,
++                                      struct i2c_msg msg[], int num)
++{
++      struct dvb_usb_device *d = i2c_get_adapdata(adap);
++      int ret = -EINVAL, j = 3000, i;
++      u8 buf[20];
++
++      if (!d || !d->desc)
++              return -ENODEV;
++
++      switch (num<<16 | d->desc->cold_ids[0]->idProduct) {
++      case 0x20000|USB_PID_TENOW_TBS5980:
++      case 0x20000|USB_PID_TENOW_TBS5925:
++      case 0x20000|USB_PID_TENOW_TBS5920:
++              buf[0] = msg[1].len;            /* length */
++              buf[1] = msg[0].addr<<1;        /* demod addr */
++              /* register */
++              buf[2] = msg[0].buf[0];
++              buf[3] = msg[0].buf[1];
++
++              ret = tbsusb_op_wr(d, 0x92, 0x91, 0, 0, buf, 4, msg[1].buf,
++                                                              msg[1].len);
++              break;
++      case 0x20000|USB_PID_TENOW_TBS5928:
++      case 0x20000|USB_PID_TENOW_TBS5921:
++              /* read */
++              buf[0] = msg[0].len;            /* length */
++              goto wrop;
++      case 0x20000|USB_PID_TENOW_TBS5910:
++              /* read */
++              buf[0] = msg[1].len;            /* length */
++wrop:         buf[1] = msg[0].addr<<1;        /* demod addr */
++              /* register */
++              buf[2] = msg[0].buf[0];
++
++              ret = tbsusb_op_wr(d, 0x90, 0x91, 5000, 0, buf, 3, msg[1].buf,
++                                                              msg[1].len);
++              break;
++      case 0x10000|USB_PID_TENOW_TBS5980:
++      case 0x10000|USB_PID_TENOW_TBS5925:
++      case 0x10000|USB_PID_TENOW_TBS5920:
++              switch (msg[0].addr) {
++              case 0x6a:
++              case 0x68:
++              case 0x61:
++              case 0x60:
++                      if (!msg[0].flags) {
++                              j = 0;
++                              goto wraddr;
++                      } else {
++                              buf[0] = msg[0].len;            /* length */
++                              buf[1] = msg[0].addr<<1;        /* addr */
++                              buf[2] = 0x00;
++                              ret = tbsusb_op_wr(d, 0x90, 0x91, 0, 0, buf, 3,
++                                                      msg[0].buf, msg[0].len);
++                      }
++                      break;
++              }
++              break;
++      case 0x10000|USB_PID_TENOW_TBS5928:
++              switch (msg[0].addr) {
++              case 0x55:
++                      if (msg[0].buf[0] == 0xf7) {
++                              /* firmware */
++                              /* Write in small blocks */
++                              buf[2] = 0xf7;
++                              goto wrfw;
++                      } else {
++                              /* write to register */
++                              j = 0;
++                              goto wraddr;
++                      }
++                      break;
++              case 0x60:
++                      /* write to register */
++                      goto wraddr;
++              }
++              break;
++      case 0x10000|USB_PID_TENOW_TBS5921:
++              switch (msg[0].addr) {
++              case 0x55:
++                      if (msg[0].buf[0] == 0xfa) {
++                              /* firmware */
++                              /* Write in small blocks */
++                              buf[2] = 0xfa;
++wrfw:                         buf[0] = 0x12;
++                              buf[1] = 0xaa;
++                              j = msg[0].len - 1;
++                              i = 1;
++                              do {
++                                      memcpy(buf + 3, msg[0].buf + i,
++                                                      j > 16 ? 16 : j);
++                                      ret = tbsusb_op_w(d, 0x80, 0, buf,
++                                                      j > 16 ? 19 : j+3);
++                                      i += 16;
++                                      j -= 16;
++                              } while (!ret && j > 0);
++                      } else {
++                              /* write to register */
++                              j = 0;
++                              goto wraddr;
++                      }
++                      break;
++              case 0x60:
++                      /* write to register */
++                      goto wraddr;
++                      break;
++              }
++              break;
++      case 0x10000|USB_PID_TENOW_TBS5910:
++              switch (msg[0].addr) {
++              case 0x61:
++              case 0x60:
++                      if (!msg[0].flags) {
++                              /* write to tuner pll */
++                              goto wraddr;
++                      }
++                      break;
++              case 0x68:
++                      /* write to stv0299 register */
++wraddr:                       if (msg[0].len+2 > sizeof(buf))
++                              break;
++                      buf[0] = msg[0].len+1;          /* length */
++                      buf[1] = msg[0].addr<<1;        /* addr */
++                      /* register */
++                      memcpy(buf+2, msg[0].buf, msg[0].len);
++                      ret = tbsusb_op_w(d, 0x80, j, buf, msg[0].len+2);
++                      break;
++              }
++              break;
++      }
++
++      if (ret)
++              return ret;
++      return num;
++}
++
++static u32 tbsusb_i2c_func(struct i2c_adapter *adapter)
++{
++      return I2C_FUNC_I2C;
++}
++
++static struct i2c_algorithm tbsusb_i2c_algo = {
++      .master_xfer   = tbsusb_i2c_transfer,
++      .functionality = tbsusb_i2c_func,
++};
++
++static int tbsusb_ci_setup(struct dvb_ca_en50221 *ca, struct dvb_usb_device **d,
++                                      struct tbsusbci_state **state, int slot)
++{
++      if (slot)
++              return -EINVAL;
++      if (ca && ca->data) {
++              *d = (struct dvb_usb_device *)ca->data;
++              if (*d && (*d)->udev && (*d)->priv) {
++                      *state = (struct tbsusbci_state *)(*d)->priv;
++                      mutex_lock(&(*state)->ca_mutex);
++                      return 0;
++              }
++      }
++      return -ENODEV;
++}
++
++static void tbsusb_ci_finish(struct tbsusbci_state *state)
++{
++      mutex_unlock(&state->ca_mutex);
++}
++
++static int tbsusb_slot_reset(struct dvb_ca_en50221 *ca, int slot)
++{
++      static u8 msg1[2] = {0x01, 0x00};
++      static u8 msg2[2] = {0x01, 0x01};
++      struct dvb_usb_device *d;
++      struct tbsusbci_state *state;
++      int ret;
++
++      ret = tbsusb_ci_setup(ca, &d, &state, slot);
++      if (ret)
++              return ret;
++
++      ret = tbsusb_op_ww(d, 0xa6, 0xa6, 5000, 0, msg1, 2, msg2, 2);
++      if (!ret)
++              msleep(1400);
++
++      tbsusb_ci_finish(state);
++
++      return ret;
++}
++
++static int tbsusb_cam_read(struct dvb_ca_en50221 *ca, int slot, int address,
++                                                              u8 where)
++{
++      struct dvb_usb_device *d;
++      struct tbsusbci_state *state;
++      int ret;
++      u8 buf[4], rbuf[1];
++
++      buf[0] = 1;
++      buf[1] = where;
++      buf[2] = (address >> 8) & 0x0f;
++      buf[3] = address;
++
++      ret = tbsusb_ci_setup(ca, &d, &state, slot);
++      if (ret)
++              return ret;
++
++      ret = tbsusb_op_wr(d, 0xa4, 0xa5, 0, 0, buf, 4, rbuf, 1);
++
++      tbsusb_ci_finish(state);
++
++      if (ret)
++              return ret;
++
++      return rbuf[0];
++}
++
++static int tbsusb_cam_write(struct dvb_ca_en50221 *ca, int slot, int address,
++                                                      u8 value, u8 where)
++{
++      struct dvb_usb_device *d;
++      struct tbsusbci_state *state;
++      int ret;
++      u8 buf[5];
++
++      buf[0] = 1;
++      buf[1] = where;
++      buf[2] = (address >> 8) & 0x0f;
++      buf[3] = address;
++      buf[4] = value;
++
++      ret = tbsusb_ci_setup(ca, &d, &state, slot);
++      if (ret)
++              return ret;
++
++      ret = tbsusb_op_w(d, 0xa2, 0, buf, 5);
++
++      tbsusb_ci_finish(state);
++
++      if (ret)
++              return ret;
++
++      return 0;
++}
++
++static int tbsusb_read_attribute_mem(struct dvb_ca_en50221 *ca,
++                                                      int slot, int address)
++{
++      return tbsusb_cam_read(ca, slot, address, 0);
++}
++
++static int tbsusb_write_attribute_mem(struct dvb_ca_en50221 *ca,
++                                              int slot, int address, u8 value)
++{
++      return tbsusb_cam_write(ca, slot, address, value, 0);
++}
++
++static int tbsusb_read_cam_control(struct dvb_ca_en50221 *ca, int slot,
++                                                              u8 address)
++{
++      return tbsusb_cam_read(ca, slot, address, 1);
++}
++
++static int tbsusb_write_cam_control(struct dvb_ca_en50221 *ca, int slot,
++                                                      u8 address, u8 value)
++{
++      return tbsusb_cam_write(ca, slot, address, value, 1);
++}
++
++static int tbsusb_set_video_port(struct dvb_ca_en50221 *ca,
++                                                      int slot, int enable)
++{
++      struct dvb_usb_device *d;
++      struct tbsusbci_state *state;
++      int ret;
++      u8 buf[2];
++
++      buf[0] = 2;
++      buf[1] = enable;
++
++      ret = tbsusb_ci_setup(ca, &d, &state, slot);
++      if (ret)
++              return ret;
++
++      ret = tbsusb_op_w(d, 0xa6, 0, buf, 2);
++
++      tbsusb_ci_finish(state);
++
++      if (ret) {
++              err("CI not %sabled.", enable ? "en" : "dis");
++              return ret;
++      }
++
++      deb_info("CI %sabled.", enable ? "en" : "dis");
++      return 0;
++}
++
++static int tbsusb_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
++{
++      return tbsusb_set_video_port(ca, slot, 0);
++}
++
++static int tbsusb_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
++{
++      return tbsusb_set_video_port(ca, slot, 1);
++}
++
++static int tbsusb_poll_slot_status(struct dvb_ca_en50221 *ca,
++                                                      int slot, int open)
++{
++      struct dvb_usb_device *d;
++      struct tbsusbci_state *state;
++      int ret;
++      u8 buf[3];
++
++      ret = tbsusb_ci_setup(ca, &d, &state, slot);
++      if (ret)
++              return 0;
++
++      ret = tbsusb_op_r(d, 0xa8, 0, buf, 3);
++
++      tbsusb_ci_finish(state);
++
++      if (ret || buf[0] != 0xa9 || buf[1] != 1 || buf[2] != 1)
++              return 0;
++
++      return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
++}
++
++static void tbsusbci_uninit(struct dvb_usb_device *d)
++{
++      struct tbsusbci_state *state;
++
++      if (d && d->priv)
++              state = (struct tbsusbci_state *)d->priv;
++      else
++              return;
++
++      if (!state || !state->ca.data)
++              return;
++
++      dvb_ca_en50221_release(&state->ca);
++      memset(&state->ca, 0, sizeof(state->ca));
++}
++
++static int tbsusbci_init(struct dvb_usb_adapter *a)
++{
++      struct tbsusbci_state *state;
++      int ret = -ENODEV;
++
++      if (a && a->dev && a->dev->priv)
++              state = (struct tbsusbci_state *)a->dev->priv;
++      else
++              goto err;
++
++      mutex_init(&state->ca_mutex);
++
++      state->ca.owner = THIS_MODULE;
++      state->ca.read_attribute_mem = tbsusb_read_attribute_mem;
++      state->ca.write_attribute_mem = tbsusb_write_attribute_mem;
++      state->ca.read_cam_control = tbsusb_read_cam_control;
++      state->ca.write_cam_control = tbsusb_write_cam_control;
++      state->ca.slot_reset = tbsusb_slot_reset;
++      state->ca.slot_shutdown = tbsusb_slot_shutdown;
++      state->ca.slot_ts_enable = tbsusb_slot_ts_enable;
++      state->ca.poll_slot_status = tbsusb_poll_slot_status;
++      state->ca.data = a->dev;
++
++      ret = dvb_ca_en50221_init(&a->dvb_adap, &state->ca, 0, 1);
++      if (ret)
++              goto err;
++
++      ret = tbsusb_poll_slot_status(&state->ca, 0, 0);
++      if (!ret)
++              ret = tbsusb_set_video_port(&state->ca, 0, 0);
++      if (ret < 0) {
++              dvb_ca_en50221_release(&state->ca);
++              goto err;
++      }
++      if (ret)
++              deb_info("CI initialized.");
++      return 0;
++
++err:  err("Cannot initialize CI: Error %d.", ret);
++      memset(&state->ca, 0, sizeof(state->ca));
++      return ret;
++}
++
++static const struct stv090x_config stv0903_config = {
++      .device                 = STV0903,
++      .demod_mode             = STV090x_SINGLE,
++      .clk_mode               = STV090x_CLK_EXT,
++
++      .xtal                   = 27000000,
++      .address                = 0x6a,
++
++      .ts1_mode               = STV090x_TSMODE_DVBCI,
++      .ts2_mode               = STV090x_TSMODE_SERIAL_CONTINUOUS,
++
++      .repeater_level         = STV090x_RPTLEVEL_16,
++      .adc1_range             = STV090x_ADC_2Vpp,
++
++      .tuner_get_frequency    = stb6100_get_frequency,
++      .tuner_set_frequency    = stb6100_set_frequency,
++      .tuner_set_bandwidth    = stb6100_set_bandwidth,
++      .tuner_get_bandwidth    = stb6100_get_bandwidth,
++
++      .set_lock_led           = tbsusb_led_ctrl,
++};
++
++static const struct stv090x_config stv0900_config = {
++      .device                 = STV0900,
++      .demod_mode             = STV090x_SINGLE,
++      .clk_mode               = STV090x_CLK_EXT,
++
++      .xtal                   = 27000000,
++      .address                = 0x68,
++
++      .ts1_mode               = STV090x_TSMODE_DVBCI,
++      .ts2_mode               = STV090x_TSMODE_SERIAL_CONTINUOUS,
++
++      .repeater_level         = STV090x_RPTLEVEL_16,
++      .adc1_range             = STV090x_ADC_2Vpp,
++
++      .tuner_get_frequency    = stb6100_get_frequency,
++      .tuner_set_frequency    = stb6100_set_frequency,
++      .tuner_set_bandwidth    = stb6100_set_bandwidth,
++      .tuner_get_bandwidth    = stb6100_get_bandwidth,
++
++      .set_lock_led           = tbsusb_led_ctrl,
++};
++
++static const struct tda10071_config tda10071_config = {
++      .demod_i2c_addr = 0x55, /* (0xaa >> 1) */
++      .tuner_i2c_addr = 0x14,
++      .i2c_wr_max     = 64,
++      .ts_mode        = TDA10071_TS_PARALLEL,
++      .spec_inv       = 0,
++      .xtal           = 40444000, /* 40.444 MHz */
++      .pll_multiplier = 20,
++      .set_lock_led   = tbsusb_led_ctrl,
++};
++
++static const struct cx24116_config cx24116_config = {
++      .demod_address   = 0x55,
++      .mpg_clk_pos_pol = 0x01,
++      .set_lock_led    = tbsusb_led_ctrl,
++};
++
++static const struct stv0288_config stv0288_config = {
++      .demod_address = 0x68,
++      .set_lock_led  = tbsusb_led_ctrl,
++};
++
++static int tbsusb_frontend_attach(struct dvb_usb_adapter *d)
++{
++      static u8 msg1[2] = {0x07, 0x01};
++      static u8 msg2[2] = {0x01, 0x01};
++      static u8 msg3[2] = {0x06, 0x01};
++      u8 *txt;
++      int ret = -ENODEV;
++
++      if (!d || !d->dev || !d->dev->desc) {
++              d->fe_adap[0].fe = NULL;
++              goto err;
++      }
++
++      switch (d->dev->desc->cold_ids[0]->idProduct) {
++      case USB_PID_TENOW_TBS5980:
++      case USB_PID_TENOW_TBS5920:
++              d->fe_adap[0].fe = dvb_attach(stv090x_attach, &stv0903_config,
++                              &d->dev->i2c_adap, STV090x_DEMODULATOR_0);
++              break;
++      case USB_PID_TENOW_TBS5925:
++              d->fe_adap[0].fe = dvb_attach(stv090x_attach, &stv0900_config,
++                              &d->dev->i2c_adap, STV090x_DEMODULATOR_0);
++              break;
++      case USB_PID_TENOW_TBS5928:
++              d->fe_adap[0].fe = dvb_attach(cx24116_attach, &cx24116_config,
++                              &d->dev->i2c_adap);
++              break;
++      case USB_PID_TENOW_TBS5910:
++              d->fe_adap[0].fe = dvb_attach(stv0288_attach, &stv0288_config,
++                              &d->dev->i2c_adap);
++              break;
++      case USB_PID_TENOW_TBS5921:
++              d->fe_adap[0].fe = dvb_attach(tda10071_attach, &tda10071_config,
++                              &d->dev->i2c_adap);
++      }
++
++      if (!d->fe_adap[0].fe)
++              goto err;
++
++      d->fe_adap[0].fe->ops.set_voltage = tbsusb_set_voltage;
++
++      switch (d->dev->desc->cold_ids[0]->idProduct) {
++      case USB_PID_TENOW_TBS5980:
++              txt = "Attached stv0903!";
++              ret = tbsusb_op_w(d->dev, 0x8a, 0, msg1, 2);
++              if (ret)
++                      goto err;
++              ret = tbsusb_op_w(d->dev, 0x8a, 0, msg2, 2);
++              if (ret)
++                      goto err;
++              ret = tbsusb_op_w(d->dev, 0x8a, 0, msg3, 2);
++              if (ret)
++                      goto err;
++              ret = tbsusbci_init(d);
++              if (ret)
++                      goto err;
++              break;
++      case USB_PID_TENOW_TBS5921:
++              txt = "Attached tda10071!";
++              ret = tbsusb_op_w(d->dev, 0x8a, 0, msg1, 2);
++              if (ret)
++                      goto err;
++              ret = tbsusb_op_w(d->dev, 0x8a, 0, msg2, 2);
++              if (ret)
++                      goto err;
++              break;
++      case USB_PID_TENOW_TBS5925:
++              txt = "Attached stv0900!";
++              ret = tbsusb_op_w(d->dev, 0x8a, 0, msg3, 2);
++              if (ret)
++                      goto err;
++              ret = tbsusb_op_w(d->dev, 0x8a, 0, msg2, 2);
++              if (ret)
++                      goto err;
++              goto wr0701;
++      case USB_PID_TENOW_TBS5920:
++              txt = "Attached stv0903!";
++              goto wr0701;
++      case USB_PID_TENOW_TBS5928:
++              txt = "Attached cx24116!";
++              goto wr0701;
++      case USB_PID_TENOW_TBS5910:
++              txt = "Attached stv0288!";
++wr0701:               ret = tbsusb_op_w(d->dev, 0x8a, 0, msg1, 2);
++              if (ret)
++                      goto err;
++              break;
++      }
++
++      deb_info(txt);
++
++err:  if (ret && d->fe_adap[0].fe) {
++              dvb_frontend_detach(d->fe_adap[0].fe);
++              d->fe_adap[0].fe = NULL;
++      }
++
++      return ret;
++}
++
++static struct stb6100_config stb6100_config = {
++      .tuner_address  = 0x60,
++      .refclock       = 27000000,
++};
++
++static int tbsusb_tuner_attach(struct dvb_usb_adapter *adap)
++{
++      int ret = -EIO;
++
++      if (!adap || !adap->dev || !adap->dev->desc || !adap->fe_adap[0].fe)
++              return -ENODEV;
++
++      switch (adap->dev->desc->cold_ids[0]->idProduct) {
++      case USB_PID_TENOW_TBS5980:
++      case USB_PID_TENOW_TBS5925:
++      case USB_PID_TENOW_TBS5920:
++              if (!dvb_attach(stb6100_attach, adap->fe_adap[0].fe,
++                              &stb6100_config, &adap->dev->i2c_adap))
++                      goto err;
++              else
++                      ret = 0;
++              deb_info("Attached stb6100!");
++              if (adap->dev->desc->cold_ids[0]->idProduct !=
++                                                      USB_PID_TENOW_TBS5925)
++                      break;
++              /* call the init function once to initialize
++                 tuner's clock output divider and demod's
++                 master clock */
++              if (adap->fe_adap[0].fe->ops.init)
++                      adap->fe_adap[0].fe->ops.init(adap->fe_adap[0].fe);
++              break;
++      case USB_PID_TENOW_TBS5910:
++              if (!dvb_attach(stb6000_attach, adap->fe_adap[0].fe, 0x61,
++                      &adap->dev->i2c_adap))
++                      goto err;
++              else
++                      ret = 0;
++              deb_info("Attached stb6000!");
++              break;
++      }
++
++err:  return ret;
++}
++
++enum tbsusb_index {
++      TBS5980_INDEX = 0,
++      TBS5925_INDEX,
++      TBS5920_INDEX,
++      TBS5928_INDEX,
++      TBS5910_INDEX,
++      TBS5921_INDEX,
++      TBSMAX_INDEX
++};
++
++static struct usb_device_id tbsusb_table[] = {
++      {USB_DEVICE(USB_VID_TENOW, USB_PID_TENOW_TBS5980)},
++      {USB_DEVICE(USB_VID_TENOW, USB_PID_TENOW_TBS5925)},
++      {USB_DEVICE(USB_VID_TENOW, USB_PID_TENOW_TBS5920)},
++      {USB_DEVICE(USB_VID_TENOW, USB_PID_TENOW_TBS5928)},
++      {USB_DEVICE(USB_VID_TENOW, USB_PID_TENOW_TBS5910)},
++      {USB_DEVICE(USB_VID_TENOW, USB_PID_TENOW_TBS5921)},
++      { }
++};
++
++MODULE_DEVICE_TABLE(usb, tbsusb_table);
++
++#define TBSUSB_DEVICE_PROPERTIES(priv, fw, intvl, tunerproc, devname, index) { \
++      .caps         = DVB_USB_IS_AN_I2C_ADAPTER, \
++      .usb_ctrl     = DEVICE_SPECIFIC, \
++      .size_of_priv = sizeof(struct priv), \
++      .firmware     = fw, \
++      .no_reconnect = 1, \
++      .i2c_algo     = &tbsusb_i2c_algo, \
++      .rc.legacy    = { \
++              .rc_map_table = tbsusb_rc_keys, \
++              .rc_map_size  = ARRAY_SIZE(tbsusb_rc_keys), \
++              .rc_interval  = intvl, \
++              .rc_query     = tbsusb_rc_query, \
++      }, \
++      .generic_bulk_ctrl_endpoint = 0x81, \
++      .num_adapters               = 1, \
++      .download_firmware          = tbsusb_load_firmware, \
++      .read_mac_address           = tbsusb_read_mac_address, \
++      .adapter                    = { { \
++              .num_frontends = 1, \
++              .fe            = { { \
++                      .frontend_attach = tbsusb_frontend_attach, \
++                      .streaming_ctrl  = NULL, \
++                      .tuner_attach    = tunerproc, \
++                      .stream          = { \
++                              .type     = USB_BULK, \
++                              .count    = 8, \
++                              .endpoint = 0x82, \
++                              .u        = { \
++                                      .bulk = { \
++                                              .buffersize = 4096, \
++                                      } \
++                              } \
++                      }, \
++              } }, \
++      } }, \
++      .num_device_descs = 1, \
++      .devices          = { { \
++              .name     = devname, \
++              .cold_ids = {&tbsusb_table[index], NULL}, \
++              .warm_ids = {NULL}, \
++      } } \
++}
++
++static struct dvb_usb_device_properties tbsusb_properties[] = {
++      TBSUSB_DEVICE_PROPERTIES(tbsusbci_state, "dvb-usb-tbsqbox-id5980.fw",
++              450, tbsusb_tuner_attach, "TBS Qbox DVB-S2 CI USB2.0 (TBS5980)",
++              TBS5980_INDEX),
++      TBSUSB_DEVICE_PROPERTIES(tbsusb_state, "dvb-usb-tbsqbox-id5925.fw",
++              250, tbsusb_tuner_attach, "TBS 5925 DVB-S2 USB2.0",
++              TBS5925_INDEX),
++      TBSUSB_DEVICE_PROPERTIES(tbsusb_state, "dvb-usb-tbsqbox-id5920.fw",
++              150, tbsusb_tuner_attach, "TBS QBOX2 DVBS USB2.0 (TBS5920)",
++              TBS5920_INDEX),
++      TBSUSB_DEVICE_PROPERTIES(tbsusb_state, "dvb-usb-tbsqbox-id5928.fw",
++              150, NULL, "TBS QBOXS2 DVBS2 USB2.0 (TBS5928)",
++              TBS5928_INDEX),
++      TBSUSB_DEVICE_PROPERTIES(tbsusb_state, "dvb-usb-tbsqbox-id5910.fw",
++              150, tbsusb_tuner_attach, "TBS QBOX DVBS USB2.0 (TBS5910)",
++              TBS5910_INDEX),
++      TBSUSB_DEVICE_PROPERTIES(tbsusb_state, "dvb-usb-tbsqbox-id5921.fw",
++              150, NULL, "TBS QBOXS3 DVBS2 USB2.0 (TBS5921)",
++              TBS5921_INDEX)
++};
++
++static int tbsusb_probe(struct usb_interface *intf,
++              const struct usb_device_id *id)
++{
++      int i;
++
++      for (i = 0; i < TBSMAX_INDEX; i++)
++              if (!dvb_usb_device_init(intf, &tbsusb_properties[i],
++                                              THIS_MODULE, NULL, adapter_nr))
++                      return 0;
++      return -ENODEV;
++}
++
++static void tbsusb_usb_disconnect(struct usb_interface *dev)
++{
++      struct dvb_usb_device *d = usb_get_intfdata(dev);
++
++      if (d && d->desc && d->desc->cold_ids[0]->idProduct ==
++                                                      USB_PID_TENOW_TBS5980)
++              tbsusbci_uninit(d);
++      dvb_usb_device_exit(dev);
++}
++
++static struct usb_driver tbsusb_driver = {
++      .name       = "tbsusb",
++      .probe      = tbsusb_probe,
++      .disconnect = tbsusb_usb_disconnect,
++      .id_table   = tbsusb_table,
++};
++
++module_usb_driver(tbsusb_driver);
++
++MODULE_AUTHOR("Various Authors");
++MODULE_DESCRIPTION("TBS 5980/5928/5925/5921/5920/5910 USB2.0 DVB-S/S2 driver");
++MODULE_VERSION("1.0");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/usb/dvb-usb/tbs-usb.h b/drivers/media/usb/dvb-usb/tbs-usb.h
+new file mode 100644
+index 0000000..2a0a112
+--- /dev/null
++++ b/drivers/media/usb/dvb-usb/tbs-usb.h
+@@ -0,0 +1,9 @@
++#ifndef _DVB_USB_TBSUSB_H_
++#define _DVB_USB_TBSUSB_H_
++
++#define DVB_USB_LOG_PREFIX "tbsusb"
++#include "dvb-usb.h"
++
++#define deb_xfer(args...) dprintk(dvb_usb_tbsusb_debug, 0x02, args)
++#define deb_info(args...) dprintk(dvb_usb_tbsusb_debug, 0x01, args)
++#endif
+-- 
+2.1.4
+
diff --git a/archive-patches/armbox/4_10_0004-TBS-fixes-for-4.6-kernel.patch b/archive-patches/armbox/4_10_0004-TBS-fixes-for-4.6-kernel.patch
new file mode 100644 (file)
index 0000000..d19e387
--- /dev/null
@@ -0,0 +1,55 @@
+From 1553b610994b399f4d42772f4a9565a4ce2a1245 Mon Sep 17 00:00:00 2001
+From: Athanasios Oikonomou <athoik@gmail.com>
+Date: Sat, 5 Mar 2016 01:34:21 +0200
+Subject: [PATCH] TBS: fixes for 4.3 kernel
+
+Change fe_sec_voltage_t to enum fe_sec_voltage.
+
+Remove TBS5921 because it uses tda10071_config that is unavailable.
+Driver should use I2C platform data now in order to load tda10071.
+More info: https://patchwork.linuxtv.org/patch/30472/
+
+diff --git a/drivers/media/usb/dvb-usb/tbs-usb.c b/drivers/media/usb/dvb-usb/tbs-usb.c
+index f142be3..98347c9 100644
+--- a/drivers/media/usb/dvb-usb/tbs-usb.c
++++ b/drivers/media/usb/dvb-usb/tbs-usb.c
+@@ -339,7 +339,7 @@ static int tbsusb_set_pin(struct dvb_frontend *fe, u8 *what)
+ }
+ static int tbsusb_set_voltage(struct dvb_frontend *fe,
+-                                              fe_sec_voltage_t voltage)
++                                              enum fe_sec_voltage voltage)
+ {
+       static u8 command_13v[2] = {0x03, 0x00};
+       static u8 command_18v[2] = {0x03, 0x01};
+@@ -787,17 +787,6 @@ static const struct stv090x_config stv0900_config = {
+       .set_lock_led           = tbsusb_led_ctrl,
+ };
+-static const struct tda10071_config tda10071_config = {
+-      .demod_i2c_addr = 0x55, /* (0xaa >> 1) */
+-      .tuner_i2c_addr = 0x14,
+-      .i2c_wr_max     = 64,
+-      .ts_mode        = TDA10071_TS_PARALLEL,
+-      .spec_inv       = 0,
+-      .xtal           = 40444000, /* 40.444 MHz */
+-      .pll_multiplier = 20,
+-      .set_lock_led   = tbsusb_led_ctrl,
+-};
+-
+ static const struct cx24116_config cx24116_config = {
+       .demod_address   = 0x55,
+       .mpg_clk_pos_pol = 0x01,
+@@ -840,9 +829,6 @@ static int tbsusb_frontend_attach(struct dvb_usb_adapter *d)
+               d->fe_adap[0].fe = dvb_attach(stv0288_attach, &stv0288_config,
+                               &d->dev->i2c_adap);
+               break;
+-      case USB_PID_TENOW_TBS5921:
+-              d->fe_adap[0].fe = dvb_attach(tda10071_attach, &tda10071_config,
+-                              &d->dev->i2c_adap);
+       }
+       if (!d->fe_adap[0].fe)
+-- 
+2.1.4
+
diff --git a/archive-patches/armbox/4_10_0005-STV-Add-PLS-support.patch b/archive-patches/armbox/4_10_0005-STV-Add-PLS-support.patch
new file mode 100644 (file)
index 0000000..1780832
--- /dev/null
@@ -0,0 +1,94 @@
+From 4bbe1b749c6f01a7a2648714f195802517e138ed Mon Sep 17 00:00:00 2001
+From: Athanasios Oikonomou <athoik@gmail.com>
+Date: Sat, 5 Mar 2016 00:32:57 +0200
+Subject: [PATCH] STV: Add PLS support
+
+
+diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c
+index fe31dd5..3a5df06 100644
+--- a/drivers/media/dvb-frontends/stv0900_core.c
++++ b/drivers/media/dvb-frontends/stv0900_core.c
+@@ -1554,12 +1554,25 @@ static int stv0900_status(struct stv0900_internal *intp,
+       return locked;
+ }
++static int stv0900_set_pls(struct stv0900_internal *intp,
++                              enum fe_stv0900_demod_num demod, u8 pls_mode, u32 pls_code)
++{
++      enum fe_stv0900_error error = STV0900_NO_ERROR;
++
++      dprintk("Set PLS code %d (mode %d)", pls_code, pls_mode);
++      stv0900_write_reg(intp, PLROOT2, (pls_mode<<2) | (pls_code>>16));
++      stv0900_write_reg(intp, PLROOT1, pls_code>>8);
++      stv0900_write_reg(intp, PLROOT0, pls_code);
++
++      return error;
++}
++
+ static int stv0900_set_mis(struct stv0900_internal *intp,
+                               enum fe_stv0900_demod_num demod, int mis)
+ {
+       dprintk("%s\n", __func__);
+-      if (mis < 0 || mis > 255) {
++      if (mis == NO_STREAM_ID_FILTER) {
+               dprintk("Disable MIS filtering\n");
+               stv0900_write_bits(intp, FILTER_EN, 0);
+       } else {
+@@ -1593,6 +1606,7 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe)
+       if (state->config->set_ts_params)
+               state->config->set_ts_params(fe, 0);
++      stv0900_set_pls(intp, demod, (c->stream_id>>26) & 0x3, (c->stream_id>>8) & 0x3FFFF);
+       stv0900_set_mis(intp, demod, c->stream_id);
+       p_result.locked = FALSE;
+diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
+index ce99b9d..264c4b8 100644
+--- a/drivers/media/dvb-frontends/stv090x.c
++++ b/drivers/media/dvb-frontends/stv090x.c
+@@ -3429,18 +3429,40 @@ err:
+       return -1;
+ }
++static int stv090x_set_pls(struct stv090x_state *state, u8 pls_mode, u32 pls_code)
++{
++      if (pls_mode == 0 && pls_code == 0)
++              pls_code = 1;
++      pls_mode &= 0x03;
++      pls_code &= 0x3FFFF;
++
++      dprintk(FE_DEBUG, 1, "Set PLS code %d (mode %d)", pls_code, pls_mode);
++      if (STV090x_WRITE_DEMOD(state, PLROOT2, (pls_mode<<2) | (pls_code>>16)) < 0)
++              goto err;
++      if (STV090x_WRITE_DEMOD(state, PLROOT1, pls_code>>8) < 0)
++              goto err;
++      if (STV090x_WRITE_DEMOD(state, PLROOT0, pls_code) < 0)
++              goto err;
++      return 0;
++err:
++      dprintk(FE_ERROR, 1, "I/O error");
++      return -1;
++}
++
+ static int stv090x_set_mis(struct stv090x_state *state, int mis)
+ {
+       u32 reg;
+-      if (mis < 0 || mis > 255) {
++      if (mis == NO_STREAM_ID_FILTER) {
+               dprintk(FE_DEBUG, 1, "Disable MIS filtering");
++              stv090x_set_pls(state, 0, 0);
+               reg = STV090x_READ_DEMOD(state, PDELCTRL1);
+               STV090x_SETFIELD_Px(reg, FILTER_EN_FIELD, 0x00);
+               if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
+                       goto err;
+       } else {
+               dprintk(FE_DEBUG, 1, "Enable MIS filtering - %d", mis);
++              stv090x_set_pls(state, (mis>>26) & 0x3, (mis>>8) & 0x3FFFF);
+               reg = STV090x_READ_DEMOD(state, PDELCTRL1);
+               STV090x_SETFIELD_Px(reg, FILTER_EN_FIELD, 0x01);
+               if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
+-- 
+2.1.4
+
diff --git a/archive-patches/armbox/4_10_0006-STV-Add-SNR-Signal-report-parameters.patch b/archive-patches/armbox/4_10_0006-STV-Add-SNR-Signal-report-parameters.patch
new file mode 100644 (file)
index 0000000..63a0757
--- /dev/null
@@ -0,0 +1,92 @@
+From 0d3b277d19137c4a0fdadfd1381f1c66515d1b0c Mon Sep 17 00:00:00 2001
+From: Athanasios Oikonomou <athoik@gmail.com>
+Date: Mon, 8 Feb 2016 22:14:31 +0200
+Subject: [PATCH] STV: Add SNR/Signal report parameters
+
+
+diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
+index 264c4b8..12fd3d0 100644
+--- a/drivers/media/dvb-frontends/stv090x.c
++++ b/drivers/media/dvb-frontends/stv090x.c
+@@ -41,6 +41,18 @@
+ static unsigned int verbose;
+ module_param(verbose, int, 0644);
++/* define how SNR measurement is reported */
++static int esno;
++module_param(esno, int, 0644);
++MODULE_PARM_DESC(esno, "SNR is reported in 0:Percentage, "\
++      "1:(EsNo dB)*10 (default:0)");
++
++/* define how signal measurement is reported */
++static int dbm;
++module_param(dbm, int, 0644);
++MODULE_PARM_DESC(dbm, "Signal is reported in 0:Percentage, "\
++      "1:-1*dBm (default:0)");
++
+ /* internal params node */
+ struct stv090x_dev {
+       /* pointer for internal params, one for each pair of demods */
+@@ -3687,7 +3699,10 @@ static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+               str = 0;
+       else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read)
+               str = -100;
+-      *strength = (str + 100) * 0xFFFF / 100;
++      if (dbm)
++              *strength = -str;
++      else
++              *strength = (str + 100) * 0xFFFF / 100;
+       return 0;
+ }
+@@ -3698,8 +3713,7 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
+       u32 reg_0, reg_1, reg, i;
+       s32 val_0, val_1, val = 0;
+       u8 lock_f;
+-      s32 div;
+-      u32 last;
++      s32 snr;
+       switch (state->delsys) {
+       case STV090x_DVBS2:
+@@ -3716,10 +3730,14 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
+                               msleep(1);
+                       }
+                       val /= 16;
+-                      last = ARRAY_SIZE(stv090x_s2cn_tab) - 1;
+-                      div = stv090x_s2cn_tab[0].read -
+-                            stv090x_s2cn_tab[last].read;
+-                      *cnr = 0xFFFF - ((val * 0xFFFF) / div);
++                      snr = stv090x_table_lookup(stv090x_s2cn_tab,
++                              ARRAY_SIZE(stv090x_s2cn_tab) - 1, val);
++                      if (snr < 0) snr = 0;
++                      if (snr > 200) snr = 200;
++                      if (esno)
++                              *cnr = snr;
++                      else
++                              *cnr = snr * 0xFFFF / 200;
+               }
+               break;
+@@ -3738,10 +3756,14 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
+                               msleep(1);
+                       }
+                       val /= 16;
+-                      last = ARRAY_SIZE(stv090x_s1cn_tab) - 1;
+-                      div = stv090x_s1cn_tab[0].read -
+-                            stv090x_s1cn_tab[last].read;
+-                      *cnr = 0xFFFF - ((val * 0xFFFF) / div);
++                      snr = stv090x_table_lookup(stv090x_s1cn_tab,
++                              ARRAY_SIZE(stv090x_s1cn_tab) - 1, val);
++                      if (snr < 0) snr = 0;
++                      if (snr > 200) snr = 200;
++                      if (esno)
++                              *cnr = snr;
++                      else
++                              *cnr = snr * 0xFFFF / 200;
+               }
+               break;
+       default:
+-- 
+2.1.4
+
diff --git a/archive-patches/armbox/4_10_0007-blindscan2.patch b/archive-patches/armbox/4_10_0007-blindscan2.patch
new file mode 100644 (file)
index 0000000..c0c98f0
--- /dev/null
@@ -0,0 +1,261 @@
+diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
+index ce99b9d..0b59a1f 100644
+--- a/drivers/media/dvb-frontends/stv090x.c
++++ b/drivers/media/dvb-frontends/stv090x.c
+@@ -1694,6 +1694,7 @@ static u32 stv090x_get_srate(struct stv090x_state *state, u32 clk)
+               ((int_1 * tmp_2) >> 16) +
+               ((int_2 * tmp_1) >> 16);
++      state->srate = srate;
+       return srate;
+ }
+@@ -2606,6 +2607,94 @@ static int stv090x_get_viterbi(struct stv090x_state *state)
+ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *state)
+ {
+       struct dvb_frontend *fe = &state->frontend;
++      struct dtv_frontend_properties *props = &fe->dtv_property_cache;
++
++      int fe_stv0900_tracking_standard_return[] = {
++              SYS_UNDEFINED,
++              SYS_DVBS,
++              SYS_DVBS2,
++              SYS_DSS
++      };
++
++      int fe_stv0900_rolloff_return[] = {
++              ROLLOFF_35,
++              ROLLOFF_25,
++              ROLLOFF_20,
++              ROLLOFF_AUTO
++      };
++
++      int fe_stv0900_modulation_return[] = {
++              QPSK,
++              PSK_8,
++              APSK_16,
++              APSK_32
++      };
++
++      int fe_stv0900_modcod_return_dvbs[] = {
++              FEC_NONE,
++              FEC_AUTO,
++              FEC_AUTO,
++              FEC_AUTO,
++              FEC_1_2,
++              FEC_3_5,
++              FEC_2_3,
++              FEC_3_4,
++              FEC_4_5,
++              FEC_5_6,
++              FEC_6_7,
++              FEC_7_8,
++              FEC_3_5,
++              FEC_2_3,
++              FEC_3_4,
++              FEC_5_6,
++              FEC_8_9,
++              FEC_9_10,
++              FEC_2_3,
++              FEC_3_4,
++              FEC_4_5,
++              FEC_5_6,
++              FEC_8_9,
++              FEC_9_10,
++              FEC_3_4,
++              FEC_4_5,
++              FEC_5_6,
++              FEC_8_9,
++              FEC_9_10,
++              FEC_AUTO
++      };
++
++      int fe_stv0900_modcod_return_dvbs2[] = {
++              FEC_NONE,
++              FEC_AUTO,
++              FEC_AUTO,
++              FEC_AUTO,
++              FEC_1_2,
++              FEC_3_5,
++              FEC_2_3,
++              FEC_3_4,
++              FEC_4_5,
++              FEC_5_6,
++              FEC_8_9,
++              FEC_9_10,
++              FEC_3_5,
++              FEC_2_3,
++              FEC_3_4,
++              FEC_5_6,
++              FEC_8_9,
++              FEC_9_10,
++              FEC_2_3,
++              FEC_3_4,
++              FEC_4_5,
++              FEC_5_6,
++              FEC_8_9,
++              FEC_9_10,
++              FEC_3_4,
++              FEC_4_5,
++              FEC_5_6,
++              FEC_8_9,
++              FEC_9_10,
++              FEC_AUTO
++      };
+       u8 tmg;
+       u32 reg;
+@@ -2645,10 +2734,71 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st
+       state->modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD);
+       state->pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01;
+       state->frame_len = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) >> 1;
+-      reg = STV090x_READ_DEMOD(state, TMGOBS);
+-      state->rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD);
+-      reg = STV090x_READ_DEMOD(state, FECM);
+-      state->inversion = STV090x_GETFIELD_Px(reg, IQINV_FIELD);
++      reg = STV090x_READ_DEMOD(state, MATSTR1);
++      state->rolloff = STV090x_GETFIELD_Px(reg, MATYPE_ROLLOFF_FIELD);
++
++      switch (state->delsys) {
++      case STV090x_DVBS2:
++              if (state->modcod <= STV090x_QPSK_910)
++                      state->modulation = STV090x_QPSK;
++              else if (state->modcod <= STV090x_8PSK_910)
++                      state->modulation = STV090x_8PSK;
++              else if (state->modcod <= STV090x_16APSK_910)
++                      state->modulation = STV090x_16APSK;
++              else if (state->modcod <= STV090x_32APSK_910)
++                      state->modulation = STV090x_32APSK;
++              else
++                      state->modulation = STV090x_UNKNOWN;
++              reg = STV090x_READ_DEMOD(state, PLHMODCOD);
++              state->inversion = STV090x_GETFIELD_Px(reg, SPECINV_DEMOD_FIELD);
++              break;
++      case STV090x_DVBS1:
++      case STV090x_DSS:
++              switch(state->fec) {
++              case STV090x_PR12:
++                      state->modcod = STV090x_QPSK_12;
++                      break;
++              case STV090x_PR23:
++                      state->modcod = STV090x_QPSK_23;
++                      break;
++              case STV090x_PR34:
++                      state->modcod = STV090x_QPSK_34;
++                      break;
++              case STV090x_PR45:
++                      state->modcod = STV090x_QPSK_45;
++                      break;
++              case STV090x_PR56:
++                      state->modcod = STV090x_QPSK_56;
++                      break;
++              case STV090x_PR67:
++                      state->modcod = STV090x_QPSK_89;
++                      break;
++              case STV090x_PR78:
++                      state->modcod = STV090x_QPSK_910;
++                      break;
++              default:
++                      state->modcod = STV090x_DUMMY_PLF;
++                      break;
++              }
++              state->modulation = STV090x_QPSK;
++              reg = STV090x_READ_DEMOD(state, FECM);
++              state->inversion = STV090x_GETFIELD_Px(reg, IQINV_FIELD);
++              break;
++      default:
++              break;
++      }
++
++      props->frequency                = state->frequency;
++      props->symbol_rate              = state->srate;
++      if (state->delsys == 2)
++              props->fec_inner        = fe_stv0900_modcod_return_dvbs2[state->modcod];
++      else
++              props->fec_inner        = fe_stv0900_modcod_return_dvbs[state->modcod];
++      props->pilot                    = state->pilots;
++      props->rolloff                  = fe_stv0900_rolloff_return[state->rolloff];
++      props->modulation               = fe_stv0900_modulation_return[state->modulation];
++      props->inversion                = state->inversion;
++      props->delivery_system          = fe_stv0900_tracking_standard_return[state->delsys];
+       if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) {
+@@ -2858,6 +3008,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
+ {
+       struct dvb_frontend *fe = &state->frontend;
++      enum stv090x_rolloff rolloff;
+       enum stv090x_modcod modcod;
+       s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0;
+@@ -2981,6 +3132,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
+       f_1 = STV090x_READ_DEMOD(state, CFR2);
+       f_0 = STV090x_READ_DEMOD(state, CFR1);
+       reg = STV090x_READ_DEMOD(state, TMGOBS);
++      rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD);
+       if (state->algo == STV090x_BLIND_SEARCH) {
+               STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00);
+@@ -3515,20 +3667,24 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe)
+       state->frequency = props->frequency;
+       state->srate = props->symbol_rate;
+       state->search_mode = STV090x_SEARCH_AUTO;
+-      state->algo = STV090x_COLD_SEARCH;
++      state->algo = STV090x_BLIND_SEARCH;
+       state->fec = STV090x_PRERR;
+-      if (state->srate > 10000000) {
+-              dprintk(FE_DEBUG, 1, "Search range: 10 MHz");
+-              state->search_range = 10000000;
+-      } else {
+-              dprintk(FE_DEBUG, 1, "Search range: 5 MHz");
+-              state->search_range = 5000000;
+-      }
++      state->search_range = 0;
+       stv090x_set_mis(state, props->stream_id);
++      dprintk(FE_DEBUG, 1, "Search started...");
+       if (stv090x_algo(state) == STV090x_RANGEOK) {
++              stv090x_get_sig_params(state);
+               dprintk(FE_DEBUG, 1, "Search success!");
++              dprintk(FE_DEBUG, 1, "frequency       = %d", props->frequency);
++              dprintk(FE_DEBUG, 1, "symbol_rate     = %d", props->symbol_rate);
++              dprintk(FE_DEBUG, 1, "fec_inner       = %d, %d", props->fec_inner, state->modcod);
++              dprintk(FE_DEBUG, 1, "pilot           = %d", props->pilot);
++              dprintk(FE_DEBUG, 1, "rolloff         = %d", props->rolloff);
++              dprintk(FE_DEBUG, 1, "modulation      = %d, %d", props->modulation, state->modulation);
++              dprintk(FE_DEBUG, 1, "inversion       = %d", props->inversion);
++              dprintk(FE_DEBUG, 1, "delivery_system = %d, %d", props->delivery_system, state->delsys);
+               return DVBFE_ALGO_SEARCH_SUCCESS;
+       } else {
+               dprintk(FE_DEBUG, 1, "Search failed!");
+@@ -3571,6 +3727,7 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
+                                       *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+                       }
+               }
++              stv090x_get_sig_params(state);
+               break;
+       case 3: /* DVB-S1/legacy mode */
+@@ -3584,6 +3742,7 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
+                                       *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+                       }
+               }
++              stv090x_get_sig_params(state);
+               break;
+       }
+diff --git a/drivers/media/dvb-frontends/stv090x_reg.h b/drivers/media/dvb-frontends/stv090x_reg.h
+index 93741ee..ac6bc30 100644
+--- a/drivers/media/dvb-frontends/stv090x_reg.h
++++ b/drivers/media/dvb-frontends/stv090x_reg.h
+@@ -1927,6 +1927,8 @@
+ #define STV090x_P1_MATSTR1                    STV090x_Px_MATSTRy(1, 1)
+ #define STV090x_P2_MATSTR0                    STV090x_Px_MATSTRy(2, 0)
+ #define STV090x_P2_MATSTR1                    STV090x_Px_MATSTRy(2, 1)
++#define STV090x_OFFST_Px_MATYPE_ROLLOFF_FIELD 0
++#define STV090x_WIDTH_Px_MATYPE_ROLLOFF_FIELD 2
+ #define STV090x_OFFST_Px_MATYPE_CURRENT_FIELD 0
+ #define STV090x_WIDTH_Px_MATYPE_CURRENT_FIELD 8
diff --git a/archive-patches/armbox/4_10_0007-stv090x-optimized-TS-sync-control.patch b/archive-patches/armbox/4_10_0007-stv090x-optimized-TS-sync-control.patch
new file mode 100644 (file)
index 0000000..1ee3cee
--- /dev/null
@@ -0,0 +1,92 @@
+From 8cc2e0072bc2dfc9a64b569e2b7bb804bf82bc55 Mon Sep 17 00:00:00 2001
+From: Athanasios Oikonomou <athoik@gmail.com>
+Date: Thu, 17 Mar 2016 06:53:34 +0200
+Subject: [PATCH] stv090x: optimized TS sync control
+
+Based on crazycat commits:
+stv090x: Minimum latence TS FIFO mode for DVB-S2.
+https://github.com/Taapat/driver/commit/b831c1a22b96ece05d0af1cc1e55d5e34d2ca13b
+stv090x: optimized TS sync control.
+https://github.com/Taapat/driver/commit/f2cacf05651efe48bb5abb02df94646a0d712362
+
+diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
+index 12fd3d0..396e0ab 100644
+--- a/drivers/media/dvb-frontends/stv090x.c
++++ b/drivers/media/dvb-frontends/stv090x.c
+@@ -3017,6 +3017,28 @@ static int stv090x_optimize_track(struct stv090x_state *state)
+       srate  = stv090x_get_srate(state, state->internal->mclk);
+       srate += stv090x_get_tmgoffst(state, srate);
++      if (state->delsys == STV090x_DVBS2 && srate > 10000000) {
++              reg = stv090x_read_reg(state, STV090x_P1_TSSTATEM);
++              STV090x_SETFIELD_Px(reg, TSOUT_NOSYNC, 1);
++              if (stv090x_write_reg(state, STV090x_P1_TSSTATEM, reg) < 0)
++                      goto err;
++
++              reg = stv090x_read_reg(state, STV090x_P1_TSSYNC);
++              STV090x_SETFIELD_Px(reg, TSFIFO_SYNCMODE, 2);
++              if (stv090x_write_reg(state, STV090x_P1_TSSYNC, reg) < 0)
++                      goto err;
++      } else {
++              reg = stv090x_read_reg(state, STV090x_P1_TSSTATEM);
++              STV090x_SETFIELD_Px(reg, TSOUT_NOSYNC, 0);
++              if (stv090x_write_reg(state, STV090x_P1_TSSTATEM, reg) < 0)
++                      goto err;
++
++              reg = stv090x_read_reg(state, STV090x_P1_TSSYNC);
++              STV090x_SETFIELD_Px(reg, TSFIFO_SYNCMODE, 0);
++              if (stv090x_write_reg(state, STV090x_P1_TSSYNC, reg) < 0)
++                      goto err;
++      }
++
+       switch (state->delsys) {
+       case STV090x_DVBS1:
+       case STV090x_DSS:
+@@ -4517,10 +4539,6 @@ static int stv0900_set_tspath(struct stv090x_state *state)
+                       case STV090x_TSMODE_DVBCI:
+                               if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x06) < 0) /* Mux'd stream mode */
+                                       goto err;
+-                              reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
+-                              STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
+-                              if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
+-                                      goto err;
+                               reg = stv090x_read_reg(state, STV090x_P2_TSCFGM);
+                               STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
+                               if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0)
+diff --git a/drivers/media/dvb-frontends/stv090x_reg.h b/drivers/media/dvb-frontends/stv090x_reg.h
+index 93741ee..c1dac9c 100644
+--- a/drivers/media/dvb-frontends/stv090x_reg.h
++++ b/drivers/media/dvb-frontends/stv090x_reg.h
+@@ -2106,6 +2106,14 @@
+ #define STV090x_WIDTH_Px_TSDIL_ON_FIELD                       1
+ #define STV090x_OFFST_Px_TSRS_ON_FIELD                        5
+ #define STV090x_WIDTH_Px_TSRS_ON_FIELD                        1
++#define STV090x_OFFST_Px_TSDESCRAMB_ON                        4
++#define STV090x_WIDTH_Px_TSDESCRAMB_ON                        1
++#define STV090x_OFFST_Px_TSFRAME_MODE                 3
++#define STV090x_WIDTH_Px_TSFRAME_MODE                 1
++#define STV090x_OFFST_Px_TS_DISABLE                   2
++#define STV090x_WIDTH_Px_TS_DISABLE                   1
++#define STV090x_OFFST_Px_TSOUT_NOSYNC                 0
++#define STV090x_WIDTH_Px_TSOUT_NOSYNC                 1
+ #define STV090x_Px_TSCFGH(__x)                                (0xF572 - (__x - 1) * 0x200)
+ #define STV090x_P1_TSCFGH                             STV090x_Px_TSCFGH(1)
+@@ -2149,6 +2157,14 @@
+ #define STV090x_OFFST_Px_TSFIFO_DPUNACT_FIELD         1
+ #define STV090x_WIDTH_Px_TSFIFO_DPUNACT_FIELD         1
++#define STV090x_Px_TSSYNC(__x)                                (0xF575 - (__x - 1) * 0x200)
++#define STV090x_P1_TSSYNC                             STV090x_Px_TSSYNC(1)
++#define STV090x_P2_TSSYNC                             STV090x_Px_TSSYNC(2)
++#define STV090x_OFFST_Px_TSFIFO_FISCR3B                       5
++#define STV090x_WIDTH_Px_TSFIFO_FISCR3B                       2
++#define STV090x_OFFST_Px_TSFIFO_SYNCMODE              3
++#define STV090x_WIDTH_Px_TSFIFO_SYNCMODE              2
++
+ #define STV090x_Px_TSINSDELH(__x)                     (0xF576 - (__x - 1) * 0x200)
+ #define STV090x_P1_TSINSDELH                          STV090x_Px_TSINSDELH(1)
+ #define STV090x_P2_TSINSDELH                          STV090x_Px_TSINSDELH(2)
+-- 
+2.1.4
+
diff --git a/archive-patches/armbox/4_10_blacklist_mmc0.patch b/archive-patches/armbox/4_10_blacklist_mmc0.patch
new file mode 100644 (file)
index 0000000..5d1027a
--- /dev/null
@@ -0,0 +1,15 @@
+diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
+index 942b42b..b678310 100644
+--- a/drivers/mmc/host/sdhci-brcmstb.c
++++ b/drivers/mmc/host/sdhci-brcmstb.c
+@@ -71,6 +71,10 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
+       struct clk *clk;
+       int res;
++      if (platform_get_resource(pdev, IORESOURCE_MEM, 0) && (unsigned int)platform_get_resource(pdev, IORESOURCE_MEM, 0)->start == 0xf03e0000) {
++              return -ENODEV;
++      }
++
+       clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "Clock not found in Device Tree\n");
diff --git a/archive-patches/armbox/4_10_reserve_dvb_adapter_0.patch b/archive-patches/armbox/4_10_reserve_dvb_adapter_0.patch
new file mode 100644 (file)
index 0000000..d42fd86
--- /dev/null
@@ -0,0 +1,13 @@
+diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
+index 560450a..eb1a212 100644
+--- a/drivers/media/dvb-core/dvbdev.c
++++ b/drivers/media/dvb-core/dvbdev.c
+@@ -789,7 +789,7 @@ static int dvbdev_check_free_adapter_num(int num)
+ static int dvbdev_get_free_adapter_num (void)
+ {
+-      int num = 0;
++      int num = 1;
+       while (num < DVB_MAX_ADAPTERS) {
+               if (dvbdev_check_free_adapter_num(num))
diff --git a/archive-patches/armbox/4_10_t230c2.patch b/archive-patches/armbox/4_10_t230c2.patch
new file mode 100644 (file)
index 0000000..c221b8a
--- /dev/null
@@ -0,0 +1,323 @@
+--- a/drivers/media/usb/dvb-usb/cxusb.c
++++ b/drivers/media/usb/dvb-usb/cxusb.c
+@@ -1402,6 +1402,76 @@
+       return 0;
+ }
++static int cxusb_mygica_t230c_frontend_attach(struct dvb_usb_adapter *adap)
++{
++      struct dvb_usb_device *d = adap->dev;
++      struct cxusb_state *st = d->priv;
++      struct i2c_adapter *adapter;
++      struct i2c_client *client_demod;
++      struct i2c_client *client_tuner;
++      struct i2c_board_info info;
++      struct si2168_config si2168_config;
++      struct si2157_config si2157_config;
++
++      /* Select required USB configuration */
++      if (usb_set_interface(d->udev, 0, 0) < 0)
++              err("set interface failed");
++
++      /* Unblock all USB pipes */
++      usb_clear_halt(d->udev,
++              usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
++      usb_clear_halt(d->udev,
++              usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
++      usb_clear_halt(d->udev,
++              usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
++
++      /* attach frontend */
++      memset(&si2168_config, 0, sizeof(si2168_config));
++      si2168_config.i2c_adapter = &adapter;
++      si2168_config.fe = &adap->fe_adap[0].fe;
++      si2168_config.ts_mode = SI2168_TS_PARALLEL;
++      si2168_config.ts_clock_inv = 1;
++      memset(&info, 0, sizeof(struct i2c_board_info));
++      strlcpy(info.type, "si2168", I2C_NAME_SIZE);
++      info.addr = 0x64;
++      info.platform_data = &si2168_config;
++      request_module(info.type);
++      client_demod = i2c_new_device(&d->i2c_adap, &info);
++      if (client_demod == NULL || client_demod->dev.driver == NULL)
++              return -ENODEV;
++
++      if (!try_module_get(client_demod->dev.driver->owner)) {
++              i2c_unregister_device(client_demod);
++              return -ENODEV;
++      }
++
++      /* attach tuner */
++      memset(&si2157_config, 0, sizeof(si2157_config));
++      si2157_config.fe = adap->fe_adap[0].fe;
++      memset(&info, 0, sizeof(struct i2c_board_info));
++      strlcpy(info.type, "si2141", I2C_NAME_SIZE);
++      info.addr = 0x60;
++      info.platform_data = &si2157_config;
++      request_module("si2157");
++      client_tuner = i2c_new_device(adapter, &info);
++      if (client_tuner == NULL || client_tuner->dev.driver == NULL) {
++              module_put(client_demod->dev.driver->owner);
++              i2c_unregister_device(client_demod);
++              return -ENODEV;
++      }
++      if (!try_module_get(client_tuner->dev.driver->owner)) {
++              i2c_unregister_device(client_tuner);
++              module_put(client_demod->dev.driver->owner);
++              i2c_unregister_device(client_demod);
++              return -ENODEV;
++      }
++
++      st->i2c_client_demod = client_demod;
++      st->i2c_client_tuner = client_tuner;
++
++      return 0;
++}
++
+ /*
+  * DViCO has shipped two devices with the same USB ID, but only one of them
+  * needs a firmware download.  Check the device class details to see if they
+@@ -1484,6 +1554,7 @@
+ static struct dvb_usb_device_properties cxusb_d680_dmb_properties;
+ static struct dvb_usb_device_properties cxusb_mygica_d689_properties;
+ static struct dvb_usb_device_properties cxusb_mygica_t230_properties;
++static struct dvb_usb_device_properties cxusb_mygica_t230c_properties;
+ static int cxusb_probe(struct usb_interface *intf,
+                      const struct usb_device_id *id)
+@@ -1516,6 +1587,8 @@
+                                    THIS_MODULE, NULL, adapter_nr) ||
+           0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties,
+                                    THIS_MODULE, NULL, adapter_nr) ||
++          0 == dvb_usb_device_init(intf, &cxusb_mygica_t230c_properties,
++                                   THIS_MODULE, NULL, adapter_nr) ||
+           0)
+               return 0;
+@@ -1567,6 +1640,8 @@
+       CONEXANT_D680_DMB,
+       MYGICA_D689,
+       MYGICA_T230,
++      MYGICA_T230C,
++      MYGICA_T230C2,
+       NR__cxusb_table_index
+ };
+@@ -1633,6 +1708,12 @@
+       },
+       [MYGICA_T230] = {
+               USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230)
++      },
++      [MYGICA_T230C] = {
++              USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230+1)
++      },
++      [MYGICA_T230C2] = {
++              USB_DEVICE(0x0572, 0xc68a)
+       },
+       {}              /* Terminating entry */
+ };
+@@ -2333,6 +2414,66 @@
+       }
+ };
++static struct dvb_usb_device_properties cxusb_mygica_t230c_properties = {
++      .caps = DVB_USB_IS_AN_I2C_ADAPTER,
++
++      .usb_ctrl         = CYPRESS_FX2,
++
++      .size_of_priv     = sizeof(struct cxusb_state),
++
++      .num_adapters = 1,
++      .adapter = {
++              {
++              .num_frontends = 1,
++              .fe = {{
++                      .streaming_ctrl   = cxusb_streaming_ctrl,
++                      .frontend_attach  = cxusb_mygica_t230c_frontend_attach,
++
++                      /* parameter for the MPEG2-data transfer */
++                      .stream = {
++                              .type = USB_BULK,
++                              .count = 5,
++                              .endpoint = 0x02,
++                              .u = {
++                                      .bulk = {
++                                              .buffersize = 8192,
++                                      }
++                              }
++                      },
++              } },
++              },
++      },
++
++      .power_ctrl       = cxusb_d680_dmb_power_ctrl,
++
++      .i2c_algo         = &cxusb_i2c_algo,
++
++      .generic_bulk_ctrl_endpoint = 0x01,
++
++#if 0 /* FIXME: crash dump kernel - dvb_usb_nec_rc_key_to_event */
++      .rc.legacy = {
++              .rc_interval    = 100,
++              .rc_map_table     = rc_map_d680_dmb_table,
++              .rc_map_size      = ARRAY_SIZE(rc_map_d680_dmb_table),
++              .rc_query         = cxusb_d680_dmb_rc_query,
++      },
++#endif
++
++      .num_device_descs = 2,
++      .devices = {
++              {
++                      "Mygica T230C DVB-T/T2/C",
++                      { NULL },
++                      { &cxusb_table[MYGICA_T230C], NULL },
++              },
++              {
++                      "Mygica T230C2 DVB-T/T2/C",
++                      { NULL },
++                      { &cxusb_table[MYGICA_T230C2], NULL },
++              },
++      }
++};
++
+ static struct usb_driver cxusb_driver = {
+       .name           = "dvb_usb_cxusb",
+       .probe          = cxusb_probe,
+diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
+index 57b250847cd3..e35b1faf0ddc 100644
+--- a/drivers/media/tuners/si2157.c
++++ b/drivers/media/tuners/si2157.c
+@@ -106,6 +106,9 @@ static int si2157_init(struct dvb_frontend *fe)
+       if (dev->chiptype == SI2157_CHIPTYPE_SI2146) {
+               memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9);
+               cmd.wlen = 9;
++      } else if (dev->chiptype == SI2157_CHIPTYPE_SI2141) {
++              memcpy(cmd.args, "\xc0\x00\x0d\x0e\x00\x01\x01\x01\x01\x03", 10);
++              cmd.wlen = 10;
+       } else {
+               memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15);
+               cmd.wlen = 15;
+@@ -115,6 +118,15 @@ static int si2157_init(struct dvb_frontend *fe)
+       if (ret)
+               goto err;
++      /* Si2141 needs a second command before it answers the revision query */
++      if (dev->chiptype == SI2157_CHIPTYPE_SI2141) {
++              memcpy(cmd.args, "\xc0\x08\x01\x02\x00\x00\x01", 7);
++              cmd.wlen = 7;
++              ret = si2157_cmd_execute(client, &cmd);
++              if (ret)
++                      goto err;
++      }
++
+       /* query chip revision */
+       memcpy(cmd.args, "\x02", 1);
+       cmd.wlen = 1;
+@@ -131,12 +143,16 @@ static int si2157_init(struct dvb_frontend *fe)
+       #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0)
+       #define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0)
+       #define SI2146_A10 ('A' << 24 | 46 << 16 | '1' << 8 | '0' << 0)
++      #define SI2141_A10 ('A' << 24 | 41 << 16 | '1' << 8 | '0' << 0)
+       switch (chip_id) {
+       case SI2158_A20:
+       case SI2148_A20:
+               fw_name = SI2158_A20_FIRMWARE;
+               break;
++      case SI2141_A10:
++              fw_name = SI2141_A10_FIRMWARE;
++              break;
+       case SI2157_A30:
+       case SI2147_A30:
+       case SI2146_A10:
+@@ -371,7 +387,7 @@ static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+ static const struct dvb_tuner_ops si2157_ops = {
+       .info = {
+-              .name           = "Silicon Labs Si2146/2147/2148/2157/2158",
++              .name           = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158",
+               .frequency_min  = 42000000,
+               .frequency_max  = 870000000,
+       },
+@@ -471,6 +487,7 @@ static int si2157_probe(struct i2c_client *client,
+ #endif
+       dev_info(&client->dev, "Silicon Labs %s successfully attached\n",
++                      dev->chiptype == SI2157_CHIPTYPE_SI2141 ?  "Si2141" :
+                       dev->chiptype == SI2157_CHIPTYPE_SI2146 ?
+                       "Si2146" : "Si2147/2148/2157/2158");
+@@ -508,6 +525,7 @@ static int si2157_remove(struct i2c_client *client)
+ static const struct i2c_device_id si2157_id_table[] = {
+       {"si2157", SI2157_CHIPTYPE_SI2157},
+       {"si2146", SI2157_CHIPTYPE_SI2146},
++      {"si2141", SI2157_CHIPTYPE_SI2141},
+       {}
+ };
+ MODULE_DEVICE_TABLE(i2c, si2157_id_table);
+@@ -524,7 +542,8 @@ static struct i2c_driver si2157_driver = {
+ module_i2c_driver(si2157_driver);
+-MODULE_DESCRIPTION("Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver");
++MODULE_DESCRIPTION("Silicon Labs Si2141/Si2146/2147/2148/2157/2158 silicon tuner driver");
+ MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+ MODULE_LICENSE("GPL");
+ MODULE_FIRMWARE(SI2158_A20_FIRMWARE);
++MODULE_FIRMWARE(SI2141_A10_FIRMWARE);
+diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h
+index d6b2c7b44053..e6436f74abaa 100644
+--- a/drivers/media/tuners/si2157_priv.h
++++ b/drivers/media/tuners/si2157_priv.h
+@@ -42,6 +42,7 @@ struct si2157_dev {
+ #define SI2157_CHIPTYPE_SI2157 0
+ #define SI2157_CHIPTYPE_SI2146 1
++#define SI2157_CHIPTYPE_SI2141 2
+ /* firmware command struct */
+ #define SI2157_ARGLEN      30
+@@ -52,5 +53,6 @@ struct si2157_cmd {
+ };
+ #define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw"
++#define SI2141_A10_FIRMWARE "dvb-tuner-si2141-a10-01.fw"
+ #endif
+--- a/drivers/media/dvb-frontends/si2168.c
++++ b/drivers/media/dvb-frontends/si2168.c
+@@ -674,6 +674,9 @@ static int si2168_probe(struct i2c_client *client,
+       case SI2168_CHIP_ID_B40:
+               dev->firmware_name = SI2168_B40_FIRMWARE;
+               break;
++      case SI2168_CHIP_ID_D60:
++              dev->firmware_name = SI2168_D60_FIRMWARE;
++              break;
+       default:
+               dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
+                       cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);
+@@ -761,3 +764,4 @@ MODULE_LICENSE("GPL");
+ MODULE_FIRMWARE(SI2168_A20_FIRMWARE);
+ MODULE_FIRMWARE(SI2168_A30_FIRMWARE);
+ MODULE_FIRMWARE(SI2168_B40_FIRMWARE);
++MODULE_FIRMWARE(SI2168_D60_FIRMWARE);
+diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h
+index 7843ccb448a0..4baa95b7d648 100644
+--- a/drivers/media/dvb-frontends/si2168_priv.h
++++ b/drivers/media/dvb-frontends/si2168_priv.h
+@@ -25,6 +25,7 @@
+ #define SI2168_A20_FIRMWARE "dvb-demod-si2168-a20-01.fw"
+ #define SI2168_A30_FIRMWARE "dvb-demod-si2168-a30-01.fw"
+ #define SI2168_B40_FIRMWARE "dvb-demod-si2168-b40-01.fw"
++#define SI2168_D60_FIRMWARE "dvb-demod-si2168-d60-01.fw"
+ #define SI2168_B40_FIRMWARE_FALLBACK "dvb-demod-si2168-02.fw"
+ /* state struct */
+@@ -37,6 +38,7 @@ struct si2168_dev {
+       #define SI2168_CHIP_ID_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0)
+       #define SI2168_CHIP_ID_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0)
+       #define SI2168_CHIP_ID_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0)
++      #define SI2168_CHIP_ID_D60 ('D' << 24 | 68 << 16 | '6' << 8 | '0' << 0)
+       unsigned int chip_id;
+       unsigned int version;
+       const char *firmware_name;