Need patches:
[PATCH 1/5] [media] mb86a20s: add i2c_gate_ctrl
[PATCH 2/5] [media] mb86a20s: Add support for TBS-Tech ISDB-T Full Seg DTB08
[PATCH 3/5] [media] Add Keytable for tbs_dtb08 remote controller
[PATCH 4/5] [media] Add support for TBS-Tech ISDB-T Full Seg DTB08


Signed-off-by: Manoel Pinheiro <pinus...@hotmail.com>
---
 drivers/media/dvb/dvb-usb/tbs-dtb08.c |  576 +++++++++++++++++++++++++++++++++
 drivers/media/dvb/dvb-usb/tbs-dtb08.h |   30 ++
 2 files changed, 606 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/dvb/dvb-usb/tbs-dtb08.c
 create mode 100644 drivers/media/dvb/dvb-usb/tbs-dtb08.h

diff --git a/drivers/media/dvb/dvb-usb/tbs-dtb08.c 
b/drivers/media/dvb/dvb-usb/tbs-dtb08.c
new file mode 100644
index 0000000..b654efd
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/tbs-dtb08.c
@@ -0,0 +1,576 @@
+/*
+ *  TBS-Tech ISDB-T Full Seg DTB08 device driver
+ *
+ *  Copyright (C) 2010-2011 Manoel Pinheiro <pinus...@pdtv.cjb.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/dvb/version.h>
+
+#include "tbs-dtb08.h"
+#include "tda18271.h"
+#include "mb86a20s.h"
+
+#define        DEMOD_I2C_ADDR  0x20
+#define        TUNER_I2C_ADDR  0xc0
+
+#ifndef USB_PID_TBS_DTB08
+#define USB_PID_TBS_DTB08      0xdb08
+#endif
+
+#define USB_VID_TBS_734C       0x734c
+
+#define TBS_DTB08_WRITE_MSG    0
+#define TBS_DTB08_READ_MSG     1
+
+#define TBS_DTB08_RC_QUERY     0xb8
+#define TBS_DTB08_LED_CONTROL  5
+
+#define FX2_IE_EX0     7
+#define FX2_EX0_ENABLE 1
+#define FX2_EX0_DISABLE        0
+#define FX2_I2CTL      6
+#define I2CTL_100Khz   0
+#define I2CTL_400Khz   1
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+static DEFINE_MUTEX(tbs_dtb08_usb_mutex);
+
+static int fx2_renum = 0;
+static unsigned char demod_ok = 0;
+
+static int tbs_dtb08_op_rw(struct usb_device *udev, u8 request, u16 value,
+                       u16 index, u8 *data, u16 len, int flags)
+{
+       int ret;
+       uint pipe;
+       u8 request_type;
+       u8 buf[64];
+
+       if ((ret = mutex_lock_interruptible(&tbs_dtb08_usb_mutex)))
+               return -EAGAIN;
+
+       if (len > 64)
+               len = 64;
+
+       pipe = (flags == TBS_DTB08_READ_MSG) ? usb_rcvctrlpipe(udev, 0)
+               : usb_sndctrlpipe(udev, 0);
+       request_type = (flags == TBS_DTB08_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
+
+       if (flags == TBS_DTB08_WRITE_MSG)
+               memcpy(buf, data, len);
+
+       ret = usb_control_msg(udev, pipe, request, request_type | 
USB_TYPE_VENDOR,
+                               value, index , buf, len, 2000);
+
+       if (flags == TBS_DTB08_READ_MSG)
+               memcpy(data, buf, len);
+
+       if (ret < 0)
+               printk(KERN_ERR "tbs_dtb08.c: %s ret=%d\n", __func__, ret);
+
+       mutex_unlock(&tbs_dtb08_usb_mutex);
+
+       return ret;
+}
+
+static int tbs_dtb08_i2c_reg_read(struct usb_device *udev, u8 addr,
+                                 u8 reg, u8 *data, u8 len)
+{
+       int i, ret;
+       u8 val, buf[64];
+
+       if (len > 64)
+               len = 64;
+       if (len < 1)
+               return -EINVAL;
+
+       for (i = 0; i < 5; i++) {
+               ret = tbs_dtb08_op_rw(udev, 0x81, 0, 0, &val, 1, 
TBS_DTB08_READ_MSG);
+               if (ret == 0 && val == 0) break;
+               msleep(1);
+       }
+
+       buf[0] = len;
+       buf[1] = addr;
+       buf[2] = reg;
+
+       ret = tbs_dtb08_op_rw(udev, 0x90, 0, 0, buf, 3, TBS_DTB08_WRITE_MSG);
+
+       for (i = 0; i < 5; i++) {
+               ret = tbs_dtb08_op_rw(udev, 0x81, 0, 0, &val, 1, 
TBS_DTB08_READ_MSG);
+               if (ret == 0 && val == 0) break;
+               msleep(1);
+       }
+
+       memset(&buf, 0, sizeof(buf));
+
+       ret = tbs_dtb08_op_rw(udev, 0x91, 0, 0, buf, len, TBS_DTB08_READ_MSG);
+       memcpy(data, buf, len);
+
+       return ret;
+}
+
+static int tbs_dtb08_i2c_reg_write(struct usb_device *udev, u8 addr, u8 reg,
+                                  u8 *data, u8 len)
+{
+       int i, ret;
+       u8 buf[64];
+
+       if (len > 62)
+               len = 62;
+       if (len < 1)
+               return -EINVAL;
+
+       buf[0] = len + 2;
+       buf[1] = addr;
+       buf[2] = reg;
+
+       memcpy(&buf[3], data, len);
+
+       for (i = 0; i < 5; i++) {
+               ret = tbs_dtb08_op_rw(udev, 0x81, 0, 0, &reg, 1, 
TBS_DTB08_READ_MSG);
+               if (ret == 0 && reg == 0) break;
+               msleep(1);
+       }
+
+       ret = tbs_dtb08_op_rw(udev, 0x80, 0, 0, buf, len + 3, 
TBS_DTB08_WRITE_MSG);
+
+       return ret;
+}
+
+static int tbs_dtb08_cmd_8a_write(struct usb_device *udev, u8 val1, u8 val2)
+{
+       u8 buf[2] = { val1, val2 };
+       int ret;
+
+       ret = tbs_dtb08_op_rw(udev, 0x8a, 0, 0, buf, 2, TBS_DTB08_WRITE_MSG);
+
+       if (ret < 0)
+               return ret;
+       else
+               return 0;
+}
+
+static int tbs_dtb08_led_control(struct i2c_adapter *adap, int onoff)
+{
+       struct dvb_usb_device *dev = i2c_get_adapdata(adap);
+
+       return tbs_dtb08_cmd_8a_write(dev->udev, TBS_DTB08_LED_CONTROL, onoff ? 
1: 0);
+}
+
+static int tbs_dtb08_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg 
msg[],
+               int num)
+{
+       struct dvb_usb_device *dev = i2c_get_adapdata(adap);
+       int ii, ret = 0;
+
+       if (!dev || !dev->udev)
+               return -ENODEV;
+
+       if (mutex_lock_interruptible(&dev->i2c_mutex) < 0)
+               return -EAGAIN;
+
+       switch (num) {
+       case 2:
+               if ((msg[0].addr == DEMOD_I2C_ADDR || msg[0].addr == 
TUNER_I2C_ADDR) &&
+                       msg[0].addr == msg[1].addr &&  msg[0].flags == 0 &&
+                       msg[1].flags == I2C_M_RD && msg[0].len == 1 && 
msg[1].len > 0) {
+                       ret = tbs_dtb08_i2c_reg_read(dev->udev, msg[0].addr,
+                                       msg[0].buf[0],msg[1].buf, msg[1].len);
+               }
+               else {
+                       printk(KERN_ERR "tbs_dtb08.c: %s num==2, "
+                               "msg[0].addr==%02x, msg[0].flags==%d, 
msg[0].len==%d "
+                               "msg[1].addr==%02x, msg[1].flags==%d, not 
suported!\n",
+                               __func__, msg[0].addr, msg[0].flags, msg[0].len,
+                               msg[1].addr, msg[1].flags);
+                       ret = -EINVAL;
+               }
+               break;
+       case 1:
+               switch (msg[0].addr) {
+               case DEMOD_I2C_ADDR:
+               case TUNER_I2C_ADDR:
+                       ii = msg[0].len - 1;
+                       if (ii < 0) {
+                               ret = -EINVAL;
+                               break;
+                       }
+                       else if (ii > 63) {
+                               ii = 63;
+                       }
+
+                       if (msg[0].flags == 0) {
+                               ret = tbs_dtb08_i2c_reg_write(
+                                       dev->udev, msg[0].addr,
+                                       msg[0].buf[0],
+                                       &msg[0].buf[1], ii);
+                       } else {
+                               ret = tbs_dtb08_i2c_reg_read(
+                                       dev->udev, msg[0].addr,
+                                       msg[0].buf[0],
+                                       msg[0].buf, msg[0].len);
+                       }
+                       break;
+               default:
+                       printk(KERN_ERR "tbs_dtb08.c: %s num==1, "
+                               "addr==%02x, not suported!\n",
+                               __func__, msg[0].addr);
+                       ret = -EINVAL;
+               }
+               break;
+       default:
+               printk(KERN_ERR "tbs_dtb08.c: %s num == %d, "
+                       "not suported!\n", __func__, num);
+               ret = -EINVAL;
+               break;
+       }
+
+       mutex_unlock(&dev->i2c_mutex);
+
+       if (ret < 0)
+               return ret;
+       else
+               return num;
+}
+
+static int tbs_dtb08_rc_query(struct dvb_usb_device *d)
+{
+       u8 buf[6];
+
+       memset (buf, 0, sizeof (buf));
+
+       tbs_dtb08_op_rw(d->udev, TBS_DTB08_RC_QUERY, 0, 0, buf,
+                       5, TBS_DTB08_READ_MSG);
+
+       if (buf[1] != (u8)~buf[2] || buf[3] != (u8)~buf[4])
+               return 0;
+
+       rc_keydown(d->rc_dev, (u16)buf[1] << 8 | buf[3], 0);
+                  
+       return 0;
+}
+
+static struct tda18271_std_map mb86a20s_tda18271_config = {
+       .dvbt_6   = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+                     .if_lvl = 7, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config tbs_dtb08_tda18271_config = {
+       .std_map = &mb86a20s_tda18271_config,
+       .gate = TDA18271_GATE_DIGITAL,
+};
+
+static int tbs_dtb08_tuner_attach(struct dvb_usb_adapter *adap)
+{
+       int ret;
+
+       if (adap == NULL || adap->fe == NULL)
+               return -ENODEV;
+
+       if (adap->fe->ops.tuner_ops.init != NULL)
+               return 0;
+
+       ret = dvb_attach(tda18271_attach, adap->fe, TUNER_I2C_ADDR,
+                        &adap->dev->i2c_adap,
+                       &tbs_dtb08_tda18271_config) == NULL ? -ENODEV : 0;
+
+       return ret;
+}
+
+static struct mb86a20s_config_regs_val mb86a20s_config_regs[] = {
+       { REG3C_IDCFG, 0x38 },
+       { REG0400_IDCFG, 0x001e },
+       { REG040E_IDCFG, 0x0032 },
+       { REG0415_IDCFG, 0x55 },
+       { REG0416_IDCFG, 0x00 },
+       { REG2820_IDCFG, 0x33dd00 },
+       { REG286A_IDCFG, 0x0002f0 },
+       { REG2874_IDCFG, 0x0001f4 },
+       { REG50D5_IDCFG, 0x00 },
+       { REG50D6_IDCFG, 0x17 },
+       { REG50B2_IDCFG, 0x3fff },
+       { REG50B4_IDCFG, 0x3fff },
+       { REG50B6_IDCFG, 0x3fff },
+       { REG50DC_IDCFG, 0x3fff },
+       { REG50DE_IDCFG, 0x3fff },
+       { REG50E0_IDCFG, 0x3fff },
+};
+
+static struct mb86a20s_config mb86a20s_cfg = {
+       .demod_address = DEMOD_I2C_ADDR,
+       .config_regs_size = ARRAY_SIZE(mb86a20s_config_regs),
+       .config_regs = mb86a20s_config_regs,
+};
+
+static int tbs_dtb08_frontend_attach(struct dvb_usb_adapter *adap)
+{
+       struct usb_device *udev = adap->dev->udev;
+
+       printk(KERN_INFO"tbs_dtb08.c: tbs_dtb08_frontend_attach(), "
+               "demod_ok=%d, fx2_renum=%d\n", demod_ok, fx2_renum);
+
+       if (adap->dev->props.adapter->tuner_attach == &tbs_dtb08_tuner_attach) {
+               if (demod_ok) {
+                       tbs_dtb08_cmd_8a_write(udev, FX2_IE_EX0, 
FX2_EX0_DISABLE);
+                       tbs_dtb08_cmd_8a_write(udev, FX2_I2CTL, I2CTL_400Khz);
+                       adap->fe = dvb_attach(mb86a20s_attach, &mb86a20s_cfg,
+                                             &adap->dev->i2c_adap);
+                       if (adap->fe) {
+                               if (tbs_dtb08_tuner_attach(adap) == 0) {
+                                       tbs_dtb08_cmd_8a_write(udev, FX2_IE_EX0,
+                                                              FX2_EX0_ENABLE);
+                                       return 0;
+                               }
+                               dvb_frontend_detach(adap->fe);
+                               adap->fe = NULL;
+                       }
+                       demod_ok = 0;
+               }
+       }
+
+       return -EIO;
+}
+
+static int tbs_dtb08_download_firmware(struct usb_device *dev,
+                                      const struct firmware *fw)
+{
+       int i, n, count;
+       u8 buf[64];
+       u8 val;
+
+       demod_ok = 0;
+
+       if (dev == NULL || fw == NULL || fw->size <= 0) {
+               printk(KERN_ERR"tbs_dtb08.c: tbs_dtb08_download_firmware(), "
+                       "invalid parameters\n");
+               return -EINVAL;
+       }
+
+       /* stop the CPU */
+       val = 1;
+       tbs_dtb08_op_rw(dev, 0xa0, 0xe600, 0, &val, 1, TBS_DTB08_WRITE_MSG);
+
+       fx2_renum = 1;
+       for (n = 0; n < fw->size; n += 0x40) {
+               count = fw->size - n;
+               if (count > 0x40)
+                       count = 0x40;
+
+               if (fx2_renum) {
+                       tbs_dtb08_op_rw(dev, 0xa0, n, 0, buf, count,
+                                       TBS_DTB08_READ_MSG);
+                       for (i = 0; i < count; i++) {
+                               if (buf[i] != fw->data[n+i]) {
+                                       fx2_renum = 0;
+                                       break;
+                               }
+                       }
+                       if (fx2_renum) continue;
+               }
+               memcpy(buf, fw->data + n, count);
+               if (tbs_dtb08_op_rw(dev, 0xa0, n, 0, buf, count,
+                               TBS_DTB08_WRITE_MSG) != count) {
+                       printk(KERN_ERR "tbs_dtb08.c: %s addr=%04x: "
+                                       "error while transferring firmware\n",
+                                       __func__, n);
+                       return -EINVAL;
+               }
+       }
+
+       /* restart the CPU */
+       val = 0;
+       if (tbs_dtb08_op_rw(dev, 0xa0, 0xe600, 0, &val, 1,
+                           TBS_DTB08_WRITE_MSG) != 1) {
+               printk(KERN_ERR "tbs_dtb08.c: %s could not restart the "
+                               "USB controller CPU.\n", __func__);
+               return -EINVAL;
+       }
+
+       if ( !fx2_renum ) { /* ReEnumeration */
+               return -EINVAL;
+       }
+
+       msleep(200);
+
+       if (tbs_dtb08_i2c_reg_read(dev, DEMOD_I2C_ADDR, 0, &demod_ok, 1) < 0)
+               goto ret_err;
+       val = 0x87;
+       if (tbs_dtb08_i2c_reg_write(dev, DEMOD_I2C_ADDR, 0x28, &val, 1) < 0)
+               goto ret_err;
+       val = 0;
+       if (tbs_dtb08_i2c_reg_read(dev, DEMOD_I2C_ADDR, 0x28, &val, 1) < 0)
+               goto ret_err;
+       if (tbs_dtb08_cmd_8a_write(dev, FX2_IE_EX0, FX2_EX0_ENABLE) < 0)
+               goto ret_err;
+       if (tbs_dtb08_op_rw(dev, TBS_DTB08_RC_QUERY, 0, 0, buf, 0x05,
+                           TBS_DTB08_READ_MSG) < 0)
+               goto ret_err;
+
+       return 0;
+
+ret_err:
+       printk(KERN_ERR "tbs_dtb08.c: %s failed!\n", __func__);
+       return -EINVAL;
+}
+
+static int tbs_dtb08_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+       return tbs_dtb08_led_control(&adap->dev->i2c_adap, onoff);
+}
+
+static u32 tbs_dtb08_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm tbs_dtb08_i2c_algo = {
+       .master_xfer = tbs_dtb08_i2c_transfer,
+       .functionality = tbs_dtb08_i2c_func,
+};
+
+
+static struct usb_device_id tbs_dtb08_table[] = {
+       { USB_DEVICE(USB_VID_TBS_734C, USB_PID_TBS_DTB08) },
+       { }     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, tbs_dtb08_table);
+
+static struct dvb_usb_device_properties tbs_dtb08_properties = {
+       .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+       .usb_ctrl = DEVICE_SPECIFIC,
+       .download_firmware = tbs_dtb08_download_firmware,
+       .firmware = "tbs-dtb08.fw",
+       .no_reconnect = 1,
+       .size_of_priv = 0,
+
+       .num_adapters = 1,
+       .adapter = {
+               {
+                       .caps = 0,
+                       .streaming_ctrl = tbs_dtb08_streaming_ctrl,
+                       .frontend_attach = tbs_dtb08_frontend_attach,
+                       .tuner_attach = tbs_dtb08_tuner_attach,
+                       .stream = {
+                               .type = USB_BULK,
+                               .count = 10,
+                               .endpoint = 0x82,
+                               .u = {
+                                       .bulk = {
+                                               .buffersize = 4096,
+                                       }
+                               }
+                       },
+               }
+       },
+
+       .rc.core = {
+               .rc_codes         = RC_MAP_TBS_DTB08,
+               .protocol         = RC_TYPE_OTHER,
+               .module_name      = "tbs_dtb08",
+               .rc_query         = tbs_dtb08_rc_query,
+               .rc_interval      = 200,
+       },
+
+       .i2c_algo = &tbs_dtb08_i2c_algo,
+       .generic_bulk_ctrl_endpoint = 0x81,
+
+       .num_device_descs = 1,
+       .devices = {
+               {
+                       .name = "TBS-Tech ISDB-T Full Seg USB 2.0 (DTB08)",
+                       .cold_ids = { &tbs_dtb08_table[0], NULL },
+                       .warm_ids = { NULL },
+               },
+               { NULL },
+       }
+};
+
+static int tbs_dtb08_probe(struct usb_interface *intf,
+               const struct usb_device_id *id)
+{
+       struct dvb_usb_device *d = NULL;
+       int ret;
+
+       printk(KERN_INFO"tbs_dtb08.c: probe interface:%d, num_altsetting=%d\n",
+              intf->cur_altsetting->desc.bInterfaceNumber, 
intf->num_altsetting);
+
+       ret = dvb_usb_device_init(intf, &tbs_dtb08_properties,
+                       THIS_MODULE, &d, adapter_nr);
+
+       if (ret != 0) {
+               printk(KERN_ERR"tbs_dtb08.c: %s failed err=%d\n", __func__, 
ret);
+               return ret;
+       }
+
+       if (d) {
+               int n;
+               for (n = 0; n < d->props.num_adapters; n++) {
+                       struct dvb_usb_adapter *adap = &d->adapter[n];
+                       if (adap && adap->fe) {
+                         ret++;
+                       }
+               }
+       }
+
+       if (ret == 0) {
+               dvb_usb_device_exit(intf);
+               return -1;
+       }
+       else
+               return 0;
+}
+
+static void tbs_dtb08_usb_device_exit(struct usb_interface *intf)
+{
+       demod_ok = 0;
+       msleep(500);
+       dvb_usb_device_exit(intf);
+}
+
+static struct usb_driver tbs_dtb08_driver = {
+       .name = "dvb-usb-tbsdtb08",
+       .probe = tbs_dtb08_probe,
+       .disconnect = tbs_dtb08_usb_device_exit,
+       .id_table = tbs_dtb08_table,
+};
+
+static int __init tbs_dtb08_module_init(void)
+{
+       int ret =  usb_register(&tbs_dtb08_driver);
+
+       if (ret)
+               printk(KERN_ERR "usb_register failed. Error number %d\n", ret);
+
+       return ret;
+}
+
+static void __exit tbs_dtb08_module_exit(void)
+{
+       /* deregister this driver from the USB subsystem */
+       usb_deregister(&tbs_dtb08_driver);
+}
+
+module_init(tbs_dtb08_module_init);
+module_exit(tbs_dtb08_module_exit);
+
+MODULE_AUTHOR("Manoel Pinheiro <pinus...@pdtv.cjb.net>");
+MODULE_DESCRIPTION("Driver for TBS-Tech ISDB-T Full Seg DTB08 Receiver");
+MODULE_VERSION("0.5.2");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/tbs-dtb08.h 
b/drivers/media/dvb/dvb-usb/tbs-dtb08.h
new file mode 100644
index 0000000..2e25485
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/tbs-dtb08.h
@@ -0,0 +1,30 @@
+/*
+ *  TBS-Tech ISDB-T Full Seg DTB08 device driver
+ *
+ *  Copyright (C) 2010-2011 Manoel Pinheiro <pinus...@pdtv.cjb.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _TBS_DTB08_H_
+#define _TBS_DTB08_H_
+
+#define DVB_USB_LOG_PREFIX "tbs_dtb08"
+
+#include "dvb-usb.h"
+
+#define deb_xfer(args...) dprintk(dvb_usb_tbsqbox_debug, 0x02, args)
+
+#endif /* _TBS_DTB08_H_ */
-- 
1.7.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to