diff --git a/packages/linux-drivers/media_build/config/generic.config b/packages/linux-drivers/media_build/config/generic.config index 06771c7814..99f6ad1557 100644 --- a/packages/linux-drivers/media_build/config/generic.config +++ b/packages/linux-drivers/media_build/config/generic.config @@ -292,6 +292,7 @@ CONFIG_DVB_USB_TBS5880=m CONFIG_DVB_USB_TBS5220=m CONFIG_DVB_USB_TBS5881=m CONFIG_DVB_USB_TBS5520=m +CONFIG_DVB_USB_TBS5580=m CONFIG_DVB_USB_TBS5927=m CONFIG_DVB_USB_TBS5520SE=m CONFIG_DVB_USB_V2=m diff --git a/packages/linux-drivers/media_build/config/usb.config b/packages/linux-drivers/media_build/config/usb.config index 6f8b65f225..e8585746de 100644 --- a/packages/linux-drivers/media_build/config/usb.config +++ b/packages/linux-drivers/media_build/config/usb.config @@ -289,6 +289,7 @@ CONFIG_DVB_USB_TBS5880=m CONFIG_DVB_USB_TBS5220=m CONFIG_DVB_USB_TBS5881=m CONFIG_DVB_USB_TBS5520=m +CONFIG_DVB_USB_TBS5580=m CONFIG_DVB_USB_TBS5927=m CONFIG_DVB_USB_TBS5520SE=m CONFIG_DVB_USB_V2=m diff --git a/packages/linux-drivers/media_build/patches/media_build-02-add-to-backports.patch b/packages/linux-drivers/media_build/patches/media_build-02-add-to-backports.patch index 496d9f1324..1395590633 100644 --- a/packages/linux-drivers/media_build/patches/media_build-02-add-to-backports.patch +++ b/packages/linux-drivers/media_build/patches/media_build-02-add-to-backports.patch @@ -1,6 +1,6 @@ --- a/backports/backports.txt +++ b/backports/backports.txt -@@ -25,6 +25,16 @@ add api_version.patch +@@ -25,6 +25,17 @@ add api_version.patch add pr_fmt.patch add debug.patch add drx39xxj.patch @@ -13,6 +13,7 @@ +add linux-260-fix-for-kernel-4.11.patch +add linux-261-fix-for-kernel-4.11-dibusb-license.patch +add linux-262-fix-for-kernel-4.11-hauppauge_dualhd_second_tuner_support.patch ++add linux-263-fix-for-kernel-4.11-tbs5580-support.patch +add cxd2880-support.patch [4.10.255] diff --git a/packages/linux-drivers/media_build/sources/backports/linux-263-fix-for-kernel-4.11-tbs5580-support.patch b/packages/linux-drivers/media_build/sources/backports/linux-263-fix-for-kernel-4.11-tbs5580-support.patch new file mode 100644 index 0000000000..deda95f9d0 --- /dev/null +++ b/packages/linux-drivers/media_build/sources/backports/linux-263-fix-for-kernel-4.11-tbs5580-support.patch @@ -0,0 +1,983 @@ +From 31df469c9e23ec37696dd4e6421cb2e2d8f00f77 Mon Sep 17 00:00:00 2001 +From: zdl <823469641@qq.com> +Date: Tue, 11 Jul 2017 18:40:23 +0800 +Subject: [PATCH] added support for TBS5580 CI box + +--- + +diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig +index bf31d7cab1d5..ecc7de956035 100644 +--- a/drivers/media/usb/dvb-usb/Kconfig ++++ b/drivers/media/usb/dvb-usb/Kconfig +@@ -440,6 +440,15 @@ config DVB_USB_TBS5520se + help + Say Y here to support the Turbosight TBS5520se USB2 DVB-T/T2/C/C2/S/S2/S2x device + ++config DVB_USB_TBS5580 ++ tristate "Turbosight TBS5580 CI support" ++ depends on DVB_USB ++ select DVB_SI2183 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_AV201X if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y here to support the Turbosight TBS5580 CI USB2 DVB-T/T2/C/C2/S/S2/S2x device ++ + config DVB_USB_TBS5927 + tristate "TurboSight TBS5927 DVB-S2 USB2.0 support" + depends on DVB_USB +diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile +index ab69673cc4b7..f5872191f45f 100644 +--- a/drivers/media/usb/dvb-usb/Makefile ++++ b/drivers/media/usb/dvb-usb/Makefile +@@ -115,6 +115,9 @@ obj-$(CONFIG_DVB_USB_TBS5520) += dvb-usb-tbs5520.o + dvb-usb-tbs5520se-objs = tbs5520se.o + obj-$(CONFIG_DVB_USB_TBS5520se) += dvb-usb-tbs5520se.o + ++dvb-usb-tbs5580-objs = tbs5580.o ++obj-$(CONFIG_DVB_USB_TBS5580) += dvb-usb-tbs5580.o ++ + dvb-usb-tbs5927-objs = tbs5927.o + obj-$(CONFIG_DVB_USB_TBS5927) += dvb-usb-tbs5927.o + +diff --git a/drivers/media/usb/dvb-usb/tbs5580.c b/drivers/media/usb/dvb-usb/tbs5580.c +new file mode 100644 +index 000000000000..4f47dc2cf64f +--- /dev/null ++++ b/drivers/media/usb/dvb-usb/tbs5580.c +@@ -0,0 +1,789 @@ ++/* ++ * TurboSight TBS 5580se driver ++ * ++ * Copyright (c) 2017 Davin zhang ++ * ++ * 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 ++#include "tbs5580.h" ++#include "si2183.h" ++#include "si2157.h" ++#include "av201x.h" ++#include "dvb_ca_en50221.h" ++ ++#define TBS5580_READ_MSG 0 ++#define TBS5580_WRITE_MSG 1 ++ ++#define TBS5580_VOLTAGE_CTRL (0x1800) ++ ++ ++struct tbs5580_state { ++ struct i2c_client *i2c_client_demod; ++ struct i2c_client *i2c_client_tuner; ++ struct dvb_ca_en50221 ca; ++ struct mutex ca_mutex; ++}; ++ ++static struct av201x_config tbs5580_av201x_cfg = { ++ .i2c_address = 0x62, ++ .id = ID_AV2018, ++ .xtal_freq = 27000, ++}; ++ ++/* debug */ ++static int dvb_usb_tbs5580_debug; ++module_param_named(debug, dvb_usb_tbs5580_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 int tbs5580_op_rw(struct usb_device *dev, u8 request, u16 value, ++ u16 index, u8 * data, u16 len, int flags) ++{ ++ int ret; ++ u8 u8buf[len]; ++ ++ unsigned int pipe = (flags == TBS5580_READ_MSG) ? ++ usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0); ++ u8 request_type = (flags == TBS5580_READ_MSG) ? USB_DIR_IN : ++ USB_DIR_OUT; ++ ++ if (flags == TBS5580_WRITE_MSG) ++ memcpy(u8buf, data, len); ++ ret = usb_control_msg(dev, pipe, request, request_type | ++ USB_TYPE_VENDOR, value, index , u8buf, len, 2000); ++ ++ if (flags == TBS5580_READ_MSG) ++ memcpy(data, u8buf, len); ++ return ret; ++} ++ ++static int tbs5580_read_attribute_mem(struct dvb_ca_en50221 *ca, ++ int slot, int address) ++{ ++ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; ++ struct tbs5580_state *state = (struct tbs5580_state *)d->priv; ++ u8 buf[4], rbuf[3]; ++ int ret; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ buf[0] = 1; ++ buf[1] = 0; ++ buf[2] = (address >> 8) & 0x0f; ++ buf[3] = address; ++ ++ //msleep(10); ++ ++ mutex_lock(&state->ca_mutex); ++ ++ ret = tbs5580_op_rw(d->udev, 0xa4, 0, 0, ++ buf, 4, TBS5580_WRITE_MSG); ++ ++ //msleep(1); ++ ++ ret = tbs5580_op_rw(d->udev, 0xa5, 0, 0, ++ rbuf, 1, TBS5580_READ_MSG); ++ ++ mutex_unlock(&state->ca_mutex); ++ ++ if (ret < 0) ++ return ret; ++ ++ return rbuf[0]; ++} ++ ++static int tbs5580_write_attribute_mem(struct dvb_ca_en50221 *ca, ++ int slot, int address, u8 value) ++{ ++ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; ++ struct tbs5580_state *state = (struct tbs5580_state *)d->priv; ++ u8 buf[5];//, rbuf[1]; ++ int ret; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ buf[0] = 1; ++ buf[1] = 0; ++ buf[2] = (address >> 8) & 0x0f; ++ buf[3] = address; ++ buf[4] = value; ++ ++ mutex_lock(&state->ca_mutex); ++ ++ ret = tbs5580_op_rw(d->udev, 0xa2, 0, 0, ++ buf, 5, TBS5580_WRITE_MSG); ++ ++ //msleep(1); ++ ++ //ret = tbs5580_op_rw(d->udev, 0xa5, 0, 0, ++ // rbuf, 1, TBS5580_READ_MSG); ++ ++ mutex_unlock(&state->ca_mutex); ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int tbs5580_read_cam_control(struct dvb_ca_en50221 *ca, int slot, ++ u8 address) ++{ ++ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; ++ struct tbs5580_state *state = (struct tbs5580_state *)d->priv; ++ u8 buf[4], rbuf[1]; ++ int ret; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ buf[0] = 1; ++ buf[1] = 1; ++ buf[2] = (address >> 8) & 0x0f; ++ buf[3] = address; ++ ++ mutex_lock(&state->ca_mutex); ++ ++ ret = tbs5580_op_rw(d->udev, 0xa4, 0, 0, ++ buf, 4, TBS5580_WRITE_MSG); ++ ++ //msleep(10); ++ ++ ret = tbs5580_op_rw(d->udev, 0xa5, 0, 0, ++ rbuf, 1, TBS5580_READ_MSG); ++ ++ mutex_unlock(&state->ca_mutex); ++ ++ if (ret < 0) ++ return ret; ++ ++ return rbuf[0]; ++} ++ ++static int tbs5580_write_cam_control(struct dvb_ca_en50221 *ca, int slot, ++ u8 address, u8 value) ++{ ++ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; ++ struct tbs5580_state *state = (struct tbs5580_state *)d->priv; ++ u8 buf[5];//, rbuf[1]; ++ int ret; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ buf[0] = 1; ++ buf[1] = 1; ++ buf[2] = (address >> 8) & 0x0f; ++ buf[3] = address; ++ buf[4] = value; ++ ++ mutex_lock(&state->ca_mutex); ++ ++ ret = tbs5580_op_rw(d->udev, 0xa2, 0, 0, ++ buf, 5, TBS5580_WRITE_MSG); ++ ++ //msleep(1); ++ ++ //ret = tbs5580_op_rw(d->udev, 0xa5, 0, 0, ++ // rbuf, 1, TBS5580_READ_MSG); ++ ++ mutex_unlock(&state->ca_mutex); ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int tbs5580_set_video_port(struct dvb_ca_en50221 *ca, ++ int slot, int enable) ++{ ++ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; ++ struct tbs5580_state *state = (struct tbs5580_state *)d->priv; ++ u8 buf[2]; ++ int ret; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ buf[0] = 2; ++ buf[1] = enable; ++ ++ mutex_lock(&state->ca_mutex); ++ ++ ret = tbs5580_op_rw(d->udev, 0xa6, 0, 0, ++ buf, 2, TBS5580_WRITE_MSG); ++ ++ mutex_unlock(&state->ca_mutex); ++ ++ if (ret < 0) ++ return ret; ++ ++ if (enable != buf[1]) { ++ err("CI not %sabled.", enable ? "en" : "dis"); ++ return -EIO; ++ } ++ ++ info("CI %sabled.", enable ? "en" : "dis"); ++ return 0; ++} ++ ++static int tbs5580_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) ++{ ++ return tbs5580_set_video_port(ca, slot, /* enable */ 0); ++} ++ ++static int tbs5580_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) ++{ ++ return tbs5580_set_video_port(ca, slot, /* enable */ 1); ++} ++ ++static int tbs5580_slot_reset(struct dvb_ca_en50221 *ca, int slot) ++{ ++ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; ++ struct tbs5580_state *state = (struct tbs5580_state *)d->priv; ++ u8 buf[2]; ++ int ret; ++ ++ if (0 != slot) { ++ return -EINVAL; ++ } ++ ++ buf[0] = 1; ++ buf[1] = 0; ++ ++ mutex_lock (&state->ca_mutex); ++ ++ ret = tbs5580_op_rw(d->udev, 0xa6, 0, 0, ++ buf, 2, TBS5580_WRITE_MSG); ++ ++ msleep (5); ++ ++ buf[1] = 1; ++ ++ ret = tbs5580_op_rw(d->udev, 0xa6, 0, 0, ++ buf, 2, TBS5580_WRITE_MSG); ++ ++ msleep (1400); ++ ++ mutex_unlock (&state->ca_mutex); ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int tbs5580_poll_slot_status(struct dvb_ca_en50221 *ca, ++ int slot, int open) ++{ ++ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; ++ struct tbs5580_state *state = (struct tbs5580_state *)d->priv; ++ u8 buf[3]; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ mutex_lock(&state->ca_mutex); ++ ++ tbs5580_op_rw(d->udev, 0xa8, 0, 0, ++ buf, 3, TBS5580_READ_MSG); ++ ++ mutex_unlock(&state->ca_mutex); ++ ++ if ((1 == buf[2]) && (1 == buf[1]) && (0xa9 == buf[0])) { ++ return (DVB_CA_EN50221_POLL_CAM_PRESENT | ++ DVB_CA_EN50221_POLL_CAM_READY); ++ } else { ++ return 0; ++ } ++} ++ ++static void tbs5580_uninit(struct dvb_usb_device *d) ++{ ++ struct tbs5580_state *state; ++ ++ if (NULL == d) ++ return; ++ ++ state = (struct tbs5580_state *)d->priv; ++ if (NULL == state) ++ return; ++ ++ if (NULL == state->ca.data) ++ return; ++ ++ /* Error ignored. */ ++ tbs5580_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0); ++ ++ dvb_ca_en50221_release(&state->ca); ++ ++ memset(&state->ca, 0, sizeof(state->ca)); ++} ++ ++static int tbs5580_init(struct dvb_usb_adapter *a) ++{ ++ ++ struct dvb_usb_device *d = a->dev; ++ struct tbs5580_state *state = (struct tbs5580_state *)d->priv; ++ int ret; ++ ++ state->ca.owner = THIS_MODULE; ++ state->ca.read_attribute_mem = tbs5580_read_attribute_mem; ++ state->ca.write_attribute_mem = tbs5580_write_attribute_mem; ++ state->ca.read_cam_control = tbs5580_read_cam_control; ++ state->ca.write_cam_control = tbs5580_write_cam_control; ++ state->ca.slot_reset = tbs5580_slot_reset; ++ state->ca.slot_shutdown = tbs5580_slot_shutdown; ++ state->ca.slot_ts_enable = tbs5580_slot_ts_enable; ++ state->ca.poll_slot_status = tbs5580_poll_slot_status; ++ state->ca.data = d; ++ ++ ret = dvb_ca_en50221_init (&a->dvb_adap, &state->ca, ++ /* flags */ 0, /* n_slots */ 1); ++ ++ if (0 != ret) { ++ err ("Cannot initialize CI: Error %d.", ret); ++ memset (&state->ca, 0, sizeof (state->ca)); ++ return ret; ++ } ++ ++ info ("CI initialized."); ++ ++ ret = tbs5580_poll_slot_status(&state->ca, 0, 0); ++ if (0 == ret) ++ tbs5580_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0); ++ ++ return 0; ++} ++ ++/* I2C */ ++static int tbs5580_i2c_transfer(struct i2c_adapter *adap, ++ struct i2c_msg msg[], int num) ++{ ++ struct dvb_usb_device *d = i2c_get_adapdata(adap); ++ struct tbs5580_state *state = (struct tbs5580_state *)d->priv; ++ int i = 0; ++ u8 buf6[20]; ++ u8 inbuf[20]; ++ ++ if (!d) ++ return -ENODEV; ++ mutex_lock(&state->ca_mutex); ++ if (mutex_lock_interruptible(&d->i2c_mutex) < 0) ++ return -EAGAIN; ++ ++ switch (num) { ++ case 2: ++ buf6[0]=msg[1].len;//lenth ++ buf6[1]=msg[0].addr<<1;//demod addr ++ //register ++ buf6[2] = msg[0].buf[0]; ++ ++ tbs5580_op_rw(d->udev, 0x90, 0, 0, ++ buf6, 3, TBS5580_WRITE_MSG); ++ //msleep(5); ++ tbs5580_op_rw(d->udev, 0x91, 0, 0, ++ inbuf, buf6[0], TBS5580_READ_MSG); ++ memcpy(msg[1].buf, inbuf, msg[1].len); ++ break; ++ case 1: ++ switch (msg[0].addr) { ++ case 0x67: ++ case 0x62: ++ case 0x61: ++ if (msg[0].flags == 0) { ++ buf6[0] = msg[0].len+1;//lenth ++ buf6[1] = msg[0].addr<<1;//addr ++ for(i=0;iudev, 0x80, 0, 0, ++ buf6, msg[0].len+2, TBS5580_WRITE_MSG); ++ } else { ++ buf6[0] = msg[0].len;//length ++ buf6[1] = (msg[0].addr<<1) | 0x01;//addr ++ tbs5580_op_rw(d->udev, 0x93, 0, 0, ++ buf6, 2, TBS5580_WRITE_MSG); ++ //msleep(5); ++ tbs5580_op_rw(d->udev, 0x91, 0, 0, ++ inbuf, buf6[0], TBS5580_READ_MSG); ++ memcpy(msg[0].buf, inbuf, msg[0].len); ++ } ++ //msleep(3); ++ break; ++ case (TBS5580_VOLTAGE_CTRL): ++ buf6[0] = 3; ++ buf6[1] = msg[0].buf[0]; ++ tbs5580_op_rw(d->udev, 0x8a, 0, 0, ++ buf6, 2, TBS5580_WRITE_MSG); ++ break; ++ } ++ ++ break; ++ } ++ ++ mutex_unlock(&d->i2c_mutex); ++ mutex_unlock(&state->ca_mutex); ++ return num; ++} ++ ++static u32 tbs5580_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static struct i2c_algorithm tbs5580_i2c_algo = { ++ .master_xfer = tbs5580_i2c_transfer, ++ .functionality = tbs5580_i2c_func, ++}; ++ ++static int tbs5580_set_voltage(struct dvb_frontend *fe, ++ enum fe_sec_voltage voltage) ++{ ++ static u8 command_13v[1] = {0x00}; ++ static u8 command_18v[1] = {0x01}; ++ struct i2c_msg msg[] = { ++ {.addr = TBS5580_VOLTAGE_CTRL, .flags = 0, ++ .buf = command_13v, .len = 1}, ++ }; ++ ++ struct dvb_usb_adapter *udev_adap = ++ (struct dvb_usb_adapter *)(fe->dvb->priv); ++ if (voltage == SEC_VOLTAGE_18) ++ msg[0].buf = command_18v; ++ ++ ++ i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1); ++ ++ return 0; ++} ++ ++static int tbs5580_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) ++{ ++ int i,ret; ++ u8 ibuf[3] = {0, 0,0}; ++ u8 eeprom[256], eepromline[16]; ++ ++ for (i = 0; i < 256; i++) { ++ ibuf[0]=1;//lenth ++ ibuf[1]=0xa0;//eeprom addr ++ ibuf[2]=i;//register ++ ret = tbs5580_op_rw(d->udev, 0x90, 0, 0, ++ ibuf, 3, TBS5580_WRITE_MSG); ++ ret = tbs5580_op_rw(d->udev, 0x91, 0, 0, ++ ibuf, 1, TBS5580_READ_MSG); ++ if (ret < 0) { ++ err("read eeprom failed."); ++ return -1; ++ } else { ++ eepromline[i%16] = ibuf[0]; ++ eeprom[i] = ibuf[0]; ++ } ++ ++ if ((i % 16) == 15) { ++ deb_xfer("%02x: ", i - 15); ++ debug_dump(eepromline, 16, deb_xfer); ++ } ++ } ++ memcpy(mac, eeprom + 16, 6); ++ return 0; ++}; ++ ++static struct dvb_usb_device_properties tbs5580_properties; ++ ++static int tbs5580_frontend_attach(struct dvb_usb_adapter *adap) ++{ ++ struct dvb_usb_device *d = adap->dev; ++ struct tbs5580_state *st = d->priv; ++ struct i2c_adapter *adapter; ++ struct i2c_client *client_demod; ++ struct i2c_client *client_tuner; ++ struct i2c_board_info info; ++ struct si2183_config si2183_config; ++ struct si2157_config si2157_config; ++ u8 buf[20]; ++ ++ mutex_init(&st->ca_mutex); ++ /* attach frontend */ ++ memset(&si2183_config,0,sizeof(si2183_config)); ++ si2183_config.i2c_adapter = &adapter; ++ si2183_config.fe = &adap->fe_adap[0].fe; ++ si2183_config.ts_mode = SI2183_TS_PARALLEL; ++ si2183_config.ts_clock_gapped = true; ++ si2183_config.rf_in = 0; ++ si2183_config.RF_switch = NULL; ++ si2183_config.agc_mode = 0x5 ; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "si2183", I2C_NAME_SIZE); ++ info.addr = 0x67; ++ info.platform_data = &si2183_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; ++ } ++ st->i2c_client_demod = client_demod; ++ ++ /* dvb core doesn't support 2 tuners for 1 demod so ++ we split the adapter in 2 frontends */ ++ ++ adap->fe_adap[0].fe2 = &adap->fe_adap[0]._fe2; ++ memcpy(adap->fe_adap[0].fe2, adap->fe_adap[0].fe, sizeof(struct dvb_frontend)); ++ ++ /* terrestrial tuner */ ++ memset(adap->fe_adap[0].fe->ops.delsys, 0, MAX_DELSYS); ++ adap->fe_adap[0].fe->ops.delsys[0] = SYS_DVBT; ++ adap->fe_adap[0].fe->ops.delsys[1] = SYS_DVBT2; ++ adap->fe_adap[0].fe->ops.delsys[2] = SYS_DVBC_ANNEX_A; ++ adap->fe_adap[0].fe->ops.delsys[3] = SYS_ISDBT; ++ adap->fe_adap[0].fe->ops.delsys[4] = SYS_DVBC_ANNEX_B; ++ ++ /* attach ter tuner */ ++ memset(&si2157_config, 0, sizeof(si2157_config)); ++ si2157_config.fe = adap->fe_adap[0].fe; ++ si2157_config.if_port = 1; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "si2157", I2C_NAME_SIZE); ++ info.addr = 0x61; ++ info.platform_data = &si2157_config; ++ request_module(info.type); ++ 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_tuner = client_tuner; ++ ++ /*attach SAT tuner*/ ++ memset(adap->fe_adap[0].fe2->ops.delsys, 0, MAX_DELSYS); ++ adap->fe_adap[0].fe2->ops.delsys[0] = SYS_DVBS; ++ adap->fe_adap[0].fe2->ops.delsys[1] = SYS_DVBS2; ++ adap->fe_adap[0].fe2->ops.delsys[2] = SYS_DSS; ++ adap->fe_adap[0].fe2->id = 1; ++ ++ if (dvb_attach(av201x_attach, adap->fe_adap[0].fe2, &tbs5580_av201x_cfg, ++ adapter) == NULL) { ++ return -ENODEV; ++ } ++ else { ++ buf[0] = 1; ++ buf[1] = 0; ++ tbs5580_op_rw(d->udev, 0x8a, 0, 0, ++ buf, 2, TBS5580_WRITE_MSG); ++ ++ adap->fe_adap[0].fe2->ops.set_voltage = tbs5580_set_voltage; ++ ++ } ++ ++ buf[0] = 0; ++ buf[1] = 0; ++ tbs5580_op_rw(d->udev, 0xb7, 0, 0, ++ buf, 2, TBS5580_WRITE_MSG); ++ buf[0] = 8; ++ buf[1] = 1; ++ tbs5580_op_rw(d->udev, 0x8a, 0, 0, ++ buf, 2, TBS5580_WRITE_MSG); ++ ++ tbs5580_init(adap); ++ ++ strlcpy(adap->fe_adap[0].fe->ops.info.name,d->props.devices[0].name,52); ++ strcat(adap->fe_adap[0].fe->ops.info.name," DVB-T/T2/C/C2/ISDB-T"); ++ strlcpy(adap->fe_adap[0].fe2->ops.info.name,d->props.devices[0].name,52); ++ strcat(adap->fe_adap[0].fe2->ops.info.name," DVB-S/S2/S2X"); ++ ++ return 0; ++} ++ ++static struct usb_device_id tbs5580_table[] = { ++ {USB_DEVICE(0x734c, 0x5580)}, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(usb, tbs5580_table); ++ ++static int tbs5580_load_firmware(struct usb_device *dev, ++ const struct firmware *frmwr) ++{ ++ u8 *b, *p; ++ int ret = 0, i; ++ u8 reset; ++ const struct firmware *fw; ++ switch (dev->descriptor.idProduct) { ++ case 0x5580: ++ ret = request_firmware(&fw, tbs5580_properties.firmware, &dev->dev); ++ if (ret != 0) { ++ err("did not find the firmware file. (%s) " ++ "Please see linux/Documentation/dvb/ for more details " ++ "on firmware-problems.", tbs5580_properties.firmware); ++ return ret; ++ } ++ break; ++ default: ++ fw = frmwr; ++ break; ++ } ++ info("start downloading TBS5580 firmware"); ++ p = kmalloc(fw->size, GFP_KERNEL); ++ reset = 1; ++ /*stop the CPU*/ ++ tbs5580_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, TBS5580_WRITE_MSG); ++ tbs5580_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, TBS5580_WRITE_MSG); ++ ++ if (p != NULL) { ++ memcpy(p, fw->data, fw->size); ++ for (i = 0; i < fw->size; i += 0x40) { ++ b = (u8 *) p + i; ++ if (tbs5580_op_rw(dev, 0xa0, i, 0, b , 0x40, ++ TBS5580_WRITE_MSG) != 0x40) { ++ err("error while transferring firmware"); ++ ret = -EINVAL; ++ break; ++ } ++ } ++ /* restart the CPU */ ++ reset = 0; ++ if (ret || tbs5580_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, ++ TBS5580_WRITE_MSG) != 1) { ++ err("could not restart the USB controller CPU."); ++ ret = -EINVAL; ++ } ++ if (ret || tbs5580_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, ++ TBS5580_WRITE_MSG) != 1) { ++ err("could not restart the USB controller CPU."); ++ ret = -EINVAL; ++ } ++ ++ msleep(100); ++ kfree(p); ++ } ++ return ret; ++} ++ ++static struct dvb_usb_device_properties tbs5580_properties = { ++ .caps = DVB_USB_IS_AN_I2C_ADAPTER, ++ .usb_ctrl = DEVICE_SPECIFIC, ++ .firmware = "dvb-usb-id5580.fw", ++ .size_of_priv = sizeof(struct tbs5580_state), ++ .no_reconnect = 1, ++ ++ .i2c_algo = &tbs5580_i2c_algo, ++ ++ .generic_bulk_ctrl_endpoint = 0x81, ++ /* parameter for the MPEG2-data transfer */ ++ .num_adapters = 1, ++ .download_firmware = tbs5580_load_firmware, ++ .read_mac_address = tbs5580_read_mac_address, ++ .adapter = {{ ++ .num_frontends = 1, ++ .fe = {{ ++ .frontend_attach = tbs5580_frontend_attach, ++ .streaming_ctrl = NULL, ++ .tuner_attach = NULL, ++ .stream = { ++ .type = USB_BULK, ++ .count = 8, ++ .endpoint = 0x82, ++ .u = { ++ .bulk = { ++ .buffersize = 4096, ++ } ++ } ++ }, ++ }}, ++ }}, ++ ++ .num_device_descs = 1, ++ .devices = { ++ {"TBS 5580 CI USB2.0", ++ {&tbs5580_table[0], NULL}, ++ {NULL}, ++ } ++ } ++}; ++ ++static int tbs5580_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ if (0 == dvb_usb_device_init(intf, &tbs5580_properties, ++ THIS_MODULE, NULL, adapter_nr)) { ++ return 0; ++ } ++ return -ENODEV; ++} ++ ++static void tbs5580_disconnect(struct usb_interface *intf) ++{ ++ ++ struct dvb_usb_device *d = usb_get_intfdata(intf); ++#if 0 ++ struct tbs5580_state *st = d->priv; ++ struct i2c_client *client; ++ ++ /* remove I2C client for tuner */ ++ client = st->i2c_client_tuner; ++ if (client) { ++ module_put(client->dev.driver->owner); ++ i2c_unregister_device(client); ++ } ++ ++ /* remove I2C client for demodulator */ ++ client = st->i2c_client_demod; ++ if (client) { ++ module_put(client->dev.driver->owner); ++ i2c_unregister_device(client); ++ } ++#endif ++ tbs5580_uninit(d); ++ dvb_usb_device_exit(intf); ++} ++ ++static struct usb_driver tbs5580_driver = { ++ .name = "tbs5580", ++ .probe = tbs5580_probe, ++ .disconnect = tbs5580_disconnect, ++ .id_table = tbs5580_table, ++}; ++ ++static int __init tbs5580_module_init(void) ++{ ++ int ret = usb_register(&tbs5580_driver); ++ if (ret) ++ err("usb_register failed. Error number %d", ret); ++ ++ return ret; ++} ++ ++static void __exit tbs5580_module_exit(void) ++{ ++ usb_deregister(&tbs5580_driver); ++} ++ ++module_init(tbs5580_module_init); ++module_exit(tbs5580_module_exit); ++ ++MODULE_AUTHOR("Davin zhang "); ++MODULE_DESCRIPTION("TurboSight TBS 5580 driver"); ++MODULE_VERSION("1.0"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/usb/dvb-usb/tbs5580.h b/drivers/media/usb/dvb-usb/tbs5580.h +new file mode 100644 +index 000000000000..e83b34c42941 +--- /dev/null ++++ b/drivers/media/usb/dvb-usb/tbs5580.h +@@ -0,0 +1,8 @@ ++#ifndef _TBS5580_H_ ++#define _TBS5580_H_ ++ ++#define DVB_USB_LOG_PREFIX "tbs5580" ++#include "dvb-usb.h" ++ ++#define deb_xfer(args...) dprintk(dvb_usb_tbs5580_debug, 0x02, args) ++#endif +diff --git a/drivers/media/dvb-frontends/si2183.c b/drivers/media/dvb-frontends/si2183.c +index d6cdff8..ea1dee0 100644 +--- a/drivers/media/dvb-frontends/si2183.c ++++ b/drivers/media/dvb-frontends/si2183.c +@@ -75,6 +75,7 @@ struct si2183_dev { + bool ts_clock_inv; + bool ts_clock_gapped; + ++ u8 agc_mode; + int fef_pin; + bool fef_inv; + int agc_pin; +@@ -427,10 +428,20 @@ static int si2183_set_dvbc(struct dvb_frontend *fe) + { + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct i2c_client *client = fe->demodulator_priv; ++ struct si2183_dev *dev = i2c_get_clientdata(client); + struct si2183_cmd cmd; + int ret; + u16 prop; + ++ memcpy(cmd.args, "\x89\x41\x06\x12\x0\x0", 6); ++ cmd.args[1]= (dev->agc_mode &0x07)<<4 |0x1; ++ cmd.wlen = 6; ++ cmd.rlen = 3; ++ ret = si2183_cmd_execute(client, &cmd); ++ if(ret){ ++ printk("Ter AGC set error !"); ++ } ++ + /* dvb-c mode */ + prop = 0x38; + ret = si2183_set_prop(client, SI2183_PROP_MODE, &prop); +@@ -551,11 +562,22 @@ static int si2183_set_dvbs(struct dvb_frontend *fe) + { + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct i2c_client *client = fe->demodulator_priv; ++ struct si2183_dev *dev = i2c_get_clientdata(client); + struct si2183_cmd cmd; + int ret; + u16 prop; + u32 pls_mode, pls_code; +- ++ ++ /*set SAT agc*/ ++ memcpy(cmd.args, "\x8a\x1d\x12\x0\x0\x0", 6); ++ cmd.args[1]= dev->agc_mode|0x18; ++ cmd.wlen = 6; ++ cmd.rlen = 3; ++ ret = si2183_cmd_execute(client, &cmd); ++ if(ret){ ++ printk("AGC set error !"); ++ } ++ + /* set mode */ + prop = 0x8; + switch (c->delivery_system) { +@@ -627,10 +649,20 @@ static int si2183_set_dvbt(struct dvb_frontend *fe) + { + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct i2c_client *client = fe->demodulator_priv; ++ struct si2183_dev *dev = i2c_get_clientdata(client); + struct si2183_cmd cmd; + int ret; + u16 prop; + ++ memcpy(cmd.args, "\x89\x41\x06\x12\x0\x0", 6); ++ cmd.args[1]= (dev->agc_mode &0x07)<<4 |0x1; ++ cmd.wlen = 6; ++ cmd.rlen = 3; ++ ret = si2183_cmd_execute(client, &cmd); ++ if(ret){ ++ printk("Ter AGC set error !"); ++ } ++ + if (c->bandwidth_hz == 0) { + return -EINVAL; + } else if (c->bandwidth_hz <= 2000000) +@@ -696,9 +728,21 @@ static int si2183_set_isdbt(struct dvb_frontend *fe) + { + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct i2c_client *client = fe->demodulator_priv; ++ struct si2183_dev *dev = i2c_get_clientdata(client); ++ struct si2183_cmd cmd; ++ + int ret; + u16 prop; + ++ memcpy(cmd.args, "\x89\x41\x06\x12\x0\x0", 6); ++ cmd.args[1]= (dev->agc_mode &0x07)<<4 |0x1; ++ cmd.wlen = 6; ++ cmd.rlen = 3; ++ ret = si2183_cmd_execute(client, &cmd); ++ if(ret){ ++ printk("Ter AGC set error !"); ++ } ++ + if (c->bandwidth_hz == 0) { + return -EINVAL; + } else if (c->bandwidth_hz <= 2000000) +@@ -1520,6 +1564,7 @@ static int si2183_probe(struct i2c_client *client, + dev->ts_mode = config->ts_mode; + dev->ts_clock_inv = config->ts_clock_inv; + dev->ts_clock_gapped = config->ts_clock_gapped; ++ dev->agc_mode = config->agc_mode; + dev->fef_pin = config->fef_pin; + dev->fef_inv = config->fef_inv; + dev->agc_pin = config->agc_pin; +diff --git a/drivers/media/dvb-frontends/si2183.h b/drivers/media/dvb-frontends/si2183.h +index 106e11c..410bbc9 100644 +--- a/drivers/media/dvb-frontends/si2183.h ++++ b/drivers/media/dvb-frontends/si2183.h +@@ -43,6 +43,8 @@ struct si2183_config { + + /* TS clock gapped */ + bool ts_clock_gapped; ++ /*agc*/ ++ u8 agc_mode; + + /* Tuner control pins */ + #define SI2183_MP_NOT_USED 1 +diff --git a/drivers/media/pci/tbsecp3/tbsecp3-dvb.c b/drivers/media/pci/tbsecp3/tbsecp3-dvb.c +index 99aef3d..1d1c8b9 100644 +--- a/drivers/media/pci/tbsecp3/tbsecp3-dvb.c ++++ b/drivers/media/pci/tbsecp3/tbsecp3-dvb.c +@@ -533,6 +533,7 @@ static int tbsecp3_frontend_attach(struct tbsecp3_adapter *adapter) + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2183", I2C_NAME_SIZE); + info.addr = adapter->nr ? 0x64 : 0x67; ++ si2183_config.agc_mode = adapter->nr? 0x4 : 0x5; + info.platform_data = &si2183_config; + request_module(info.type); + client_demod = i2c_new_device(i2c, &info);