You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1334 lines
35 KiB
1334 lines
35 KiB
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
|
|
|
|
|