V4L/DVB (8972): initial driver for af9015 chipset

- initial driver for the Afatech AF9015 chipset

Thanks-to: Mark Spieth <mark@digivation.com.au>
Thanks-to: Lee Essen <lee.essen@nowonline.co.uk>
Thanks-to: Luca Olivetti <luca@ventoso.org>
Thanks-to: Andrew Leech <andrew@floppyspongeonline.com>
Thanks-to: Nick Andrew <nick-linuxtv@nick-andrew.net>
Thanks-to: Rafael Antoniello <rafael.antoniello@gmail.com>
Thanks-to: Jarryd Beck <jarro.2783@gmail.com>
Thanks-to: Jose Alberto Reguero <jareguero@telefonica.net>
Thanks-to: Benjamin Larsson <banan@ludd.ltu.se>
Thanks-to: Wolfgang Breyha <wbreyha@gmx.net>
Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 1d9e98c..b35d7f0 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -271,3 +271,15 @@
 	select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
+
+config DVB_USB_AF9015
+	tristate "Afatech AF9015 DVB-T USB2.0 support"
+	depends on DVB_USB && EXPERIMENTAL
+	select DVB_AF9013
+	select DVB_PLL              if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2060   if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_QT1010   if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+	help
+	  Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 116544f..ece8c9d 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -70,6 +70,9 @@
 dvb-usb-dtv5100-objs = dtv5100.o
 obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o
 
+dvb-usb-af9015-objs = af9015.o
+obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o
+
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
 # due to tuner-xc3028
 EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
new file mode 100644
index 0000000..a284648
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -0,0 +1,1459 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ *    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 "af9015.h"
+#include "af9013.h"
+#include "mt2060.h"
+#include "qt1010.h"
+#include "tda18271.h"
+#include "mxl5005s.h"
+#if 0
+#include "mc44s80x.h"
+#endif
+
+int dvb_usb_af9015_debug;
+module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+int dvb_usb_af9015_remote;
+module_param_named(remote, dvb_usb_af9015_remote, int, 0644);
+MODULE_PARM_DESC(remote, "select remote");
+int dvb_usb_af9015_dual_mode;
+module_param_named(dual_mode, dvb_usb_af9015_dual_mode, int, 0644);
+MODULE_PARM_DESC(dual_mode, "enable dual mode");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static DEFINE_MUTEX(af9015_usb_mutex);
+
+static struct af9015_config af9015_config;
+static struct dvb_usb_device_properties af9015_properties[2];
+int af9015_properties_count = ARRAY_SIZE(af9015_properties);
+
+static struct af9013_config af9015_af9013_config[] = {
+	{
+		.demod_address = AF9015_I2C_DEMOD,
+		.output_mode = AF9013_OUTPUT_MODE_USB,
+		.api_version = { 0, 1, 9, 0 },
+		.gpio[0] = AF9013_GPIO_HI,
+		.gpio[1] = AF9013_GPIO_LO,
+		.gpio[3] = AF9013_GPIO_TUNER_ON,
+
+	}, {
+		.output_mode = AF9013_OUTPUT_MODE_SERIAL,
+		.api_version = { 0, 1, 9, 0 },
+		.gpio[0] = AF9013_GPIO_TUNER_ON,
+		.gpio[1] = AF9013_GPIO_LO,
+	}
+};
+
+static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
+{
+	int act_len, ret;
+	u8 buf[64];
+	u8 write = 1;
+	u8 msg_len = 8;
+	static u8 seq; /* packet sequence number */
+
+	if (mutex_lock_interruptible(&af9015_usb_mutex) < 0)
+		return -EAGAIN;
+
+	buf[0] = req->cmd;
+	buf[1] = seq++;
+	buf[2] = req->i2c_addr;
+	buf[3] = req->addr >> 8;
+	buf[4] = req->addr & 0xff;
+	buf[5] = req->mbox;
+	buf[6] = req->addr_len;
+	buf[7] = req->data_len;
+
+	switch (req->cmd) {
+	case GET_CONFIG:
+	case BOOT:
+	case READ_MEMORY:
+	case RECONNECT_USB:
+	case GET_IR_CODE:
+		write = 0;
+		break;
+	case READ_I2C:
+		write = 0;
+		buf[2] |= 0x01; /* set I2C direction */
+	case WRITE_I2C:
+		buf[0] = READ_WRITE_I2C;
+		break;
+	case WRITE_MEMORY:
+		if (((req->addr & 0xff00) == 0xff00) ||
+		    ((req->addr & 0xae00) == 0xae00))
+			buf[0] = WRITE_VIRTUAL_MEMORY;
+	case WRITE_VIRTUAL_MEMORY:
+	case COPY_FIRMWARE:
+	case DOWNLOAD_FIRMWARE:
+		break;
+	default:
+		err("unknown command:%d", req->cmd);
+		ret = -1;
+		goto error_unlock;
+	}
+
+	/* write requested */
+	if (write) {
+		memcpy(&buf[8], req->data, req->data_len);
+		msg_len += req->data_len;
+	}
+	deb_xfer(">>> ");
+	debug_dump(buf, msg_len, deb_xfer);
+
+	/* send req */
+	ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
+	&act_len, AF9015_USB_TIMEOUT);
+	if (ret)
+		err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len);
+	else
+		if (act_len != msg_len)
+			ret = -1; /* all data is not send */
+	if (ret)
+		goto error_unlock;
+
+	/* no ack for those packets */
+	if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
+		goto exit_unlock;
+
+	/* receive ack and data if read req */
+	msg_len = 1 + 1 + req->data_len;  /* seq + status + data len */
+	ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
+			   &act_len, AF9015_USB_TIMEOUT);
+	if (ret) {
+		err("recv bulk message failed:%d", ret);
+		ret = -1;
+		goto error_unlock;
+	}
+
+	deb_xfer("<<< ");
+	debug_dump(buf, act_len, deb_xfer);
+
+	/* remote controller query status is 1 if remote code is not received */
+	if (req->cmd == GET_IR_CODE && buf[1] == 1) {
+		buf[1] = 0; /* clear command "error" status */
+		memset(&buf[2], 0, req->data_len);
+		buf[3] = 1; /* no remote code received mark */
+	}
+
+	/* check status */
+	if (buf[1]) {
+		err("command failed:%d", buf[1]);
+		ret = -1;
+		goto error_unlock;
+	}
+
+	/* read request, copy returned data to return buf */
+	if (!write)
+		memcpy(req->data, &buf[2], req->data_len);
+
+error_unlock:
+exit_unlock:
+	mutex_unlock(&af9015_usb_mutex);
+
+	return ret;
+}
+
+static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
+{
+	return af9015_rw_udev(d->udev, req);
+}
+
+static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val,
+	u8 len)
+{
+	struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
+		val};
+	return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val)
+{
+	return af9015_write_regs(d, addr, &val, 1);
+}
+
+static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val)
+{
+	struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, 1, val};
+	return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+	u8 val)
+{
+	struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val};
+
+	if (addr == af9015_af9013_config[0].demod_address ||
+	    addr == af9015_af9013_config[1].demod_address)
+		req.addr_len = 3;
+
+	return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+	u8 *val)
+{
+	struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val};
+
+	if (addr == af9015_af9013_config[0].demod_address ||
+	    addr == af9015_af9013_config[1].demod_address)
+		req.addr_len = 3;
+
+	return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+	int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	int ret = 0, i = 0;
+	u16 addr;
+	u8 mbox, addr_len;
+	struct req_t req;
+
+/* TODO: implement bus lock
+
+The bus lock is needed because there is two tuners both using same I2C-address.
+Due to that the only way to select correct tuner is use demodulator I2C-gate.
+
+................................................
+. AF9015 includes integrated AF9013 demodulator.
+. ____________                   ____________  .                ____________
+.|     uC     |                 |   demod    | .               |    tuner   |
+.|------------|                 |------------| .               |------------|
+.|   AF9015   |                 |  AF9013/5  | .               |   MXL5003  |
+.|            |--+----I2C-------|-----/ -----|-.-----I2C-------|            |
+.|            |  |              | addr 0x38  | .               |  addr 0xc6 |
+.|____________|  |              |____________| .               |____________|
+.................|..............................
+		 |               ____________                   ____________
+		 |              |   demod    |                 |    tuner   |
+		 |              |------------|                 |------------|
+		 |              |   AF9013   |                 |   MXL5003  |
+		 +----I2C-------|-----/ -----|-------I2C-------|            |
+				| addr 0x3a  |                 |  addr 0xc6 |
+				|____________|                 |____________|
+*/
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	while (i < num) {
+		if (msg[i].addr == af9015_af9013_config[0].demod_address ||
+		    msg[i].addr == af9015_af9013_config[1].demod_address) {
+			addr = msg[i].buf[0] << 8;
+			addr += msg[i].buf[1];
+			mbox = msg[i].buf[2];
+			addr_len = 3;
+		} else {
+			addr = msg[i].buf[0];
+			addr_len = 1;
+			mbox = 0;
+		}
+
+		if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+			if (msg[i].addr ==
+				af9015_af9013_config[0].demod_address)
+				req.cmd = READ_MEMORY;
+			else
+				req.cmd = READ_I2C;
+			req.i2c_addr = msg[i].addr;
+			req.addr = addr;
+			req.mbox = mbox;
+			req.addr_len = addr_len;
+			req.data_len = msg[i+1].len;
+			req.data = &msg[i+1].buf[0];
+			ret = af9015_ctrl_msg(d, &req);
+			i += 2;
+		} else {
+			if (msg[i].addr ==
+				af9015_af9013_config[0].demod_address)
+				req.cmd = WRITE_MEMORY;
+			else
+				req.cmd = WRITE_I2C;
+			req.i2c_addr = msg[i].addr;
+			req.addr = addr;
+			req.mbox = mbox;
+			req.addr_len = addr_len;
+			req.data_len = msg[i].len-addr_len;
+			req.data = &msg[i].buf[addr_len];
+			ret = af9015_ctrl_msg(d, &req);
+			i += 1;
+		}
+		if (ret)
+			goto error;
+
+	}
+	ret = i;
+
+error:
+	mutex_unlock(&d->i2c_mutex);
+
+	return ret;
+}
+
+static u32 af9015_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm af9015_i2c_algo = {
+	.master_xfer = af9015_i2c_xfer,
+	.functionality = af9015_i2c_func,
+};
+
+static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op)
+{
+	int ret;
+	u8 val, mask = 0x01;
+
+	ret = af9015_read_reg(d, addr, &val);
+	if (ret)
+		return ret;
+
+	mask <<= bit;
+	if (op) {
+		/* set bit */
+		val |= mask;
+	} else {
+		/* clear bit */
+		mask ^= 0xff;
+		val &= mask;
+	}
+
+	return af9015_write_reg(d, addr, val);
+}
+
+static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+	return af9015_do_reg_bit(d, addr, bit, 1);
+}
+
+static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+	return af9015_do_reg_bit(d, addr, bit, 0);
+}
+
+static int af9015_init_endpoint(struct dvb_usb_device *d)
+{
+	int ret;
+	u16 frame_size;
+	u8  packet_size;
+	deb_info("%s: USB speed:%d\n", __func__, d->udev->speed);
+
+#define TS_PACKET_SIZE            188
+
+#define TS_USB20_PACKET_COUNT     348
+#define TS_USB20_FRAME_SIZE       (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
+
+#define TS_USB11_PACKET_COUNT      21
+#define TS_USB11_FRAME_SIZE       (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
+
+#define TS_USB20_MAX_PACKET_SIZE  512
+#define TS_USB11_MAX_PACKET_SIZE   64
+
+	if (d->udev->speed == USB_SPEED_FULL) {
+		frame_size = TS_USB11_FRAME_SIZE/4;
+		packet_size = TS_USB11_MAX_PACKET_SIZE/4;
+	} else {
+		frame_size = TS_USB20_FRAME_SIZE/4;
+		packet_size = TS_USB20_MAX_PACKET_SIZE/4;
+	}
+
+	ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */
+	if (ret)
+		goto error;
+	ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */
+	if (ret)
+		goto error;
+	ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */
+	if (ret)
+		goto error;
+	ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */
+	if (ret)
+		goto error;
+	ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */
+	if (ret)
+		goto error;
+	if (af9015_config.dual_mode) {
+		ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */
+		if (ret)
+			goto error;
+	}
+	ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */
+	if (ret)
+		goto error;
+	if (af9015_config.dual_mode) {
+		ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */
+		if (ret)
+			goto error;
+	}
+	/* EP4 xfer length */
+	ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff);
+	if (ret)
+		goto error;
+	ret = af9015_write_reg(d, 0xdd89, frame_size >> 8);
+	if (ret)
+		goto error;
+	/* EP5 xfer length */
+	ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff);
+	if (ret)
+		goto error;
+	ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8);
+	if (ret)
+		goto error;
+	ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */
+	if (ret)
+		goto error;
+	ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */
+	if (ret)
+		goto error;
+	ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */
+	if (ret)
+		goto error;
+	if (af9015_config.dual_mode) {
+		ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */
+		if (ret)
+			goto error;
+	}
+
+	/* enable / disable mp2if2 */
+	if (af9015_config.dual_mode)
+		ret = af9015_set_reg_bit(d, 0xd50b, 0);
+	else
+		ret = af9015_clear_reg_bit(d, 0xd50b, 0);
+error:
+	if (ret)
+		err("endpoint init failed:%d", ret);
+	return ret;
+}
+
+static int af9015_copy_firmware(struct dvb_usb_device *d)
+{
+	int ret;
+	u8 fw_params[4];
+	u8 val, i;
+	struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params),
+		fw_params };
+	deb_info("%s:\n", __func__);
+
+	fw_params[0] = af9015_config.firmware_size >> 8;
+	fw_params[1] = af9015_config.firmware_size & 0xff;
+	fw_params[2] = af9015_config.firmware_checksum >> 8;
+	fw_params[3] = af9015_config.firmware_checksum & 0xff;
+
+	/* wait 2nd demodulator ready */
+	msleep(100);
+
+	ret = af9015_read_reg_i2c(d, 0x3a, 0x98be, &val);
+	if (ret)
+		goto error;
+	else
+		deb_info("%s: firmware status:%02x\n", __func__, val);
+
+	if (val == 0x0c) /* fw is running, no need for download */
+		goto exit;
+
+	/* set I2C master clock to fast (to speed up firmware copy) */
+	ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */
+	if (ret)
+		goto error;
+
+	msleep(50);
+
+	/* copy firmware */
+	ret = af9015_ctrl_msg(d, &req);
+	if (ret)
+		err("firmware copy cmd failed:%d", ret);
+	deb_info("%s: firmware copy done\n", __func__);
+
+	/* set I2C master clock back to normal */
+	ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */
+	if (ret)
+		goto error;
+
+	/* request boot firmware */
+	ret = af9015_write_reg_i2c(d, af9015_af9013_config[1].demod_address,
+		0xe205, 1);
+	deb_info("%s: firmware boot cmd status:%d\n", __func__, ret);
+	if (ret)
+		goto error;
+
+	for (i = 0; i < 15; i++) {
+		msleep(100);
+
+		/* check firmware status */
+		ret = af9015_read_reg_i2c(d,
+			af9015_af9013_config[1].demod_address, 0x98be, &val);
+		deb_info("%s: firmware status cmd status:%d fw status:%02x\n",
+			__func__, ret, val);
+		if (ret)
+			goto error;
+
+		if (val == 0x0c || val == 0x04) /* success or fail */
+			break;
+	}
+
+	if (val == 0x04) {
+		err("firmware did not run");
+		ret = -1;
+	} else if (val != 0x0c) {
+		err("firmware boot timeout");
+		ret = -1;
+	}
+
+error:
+exit:
+	return ret;
+}
+
+/* dump eeprom */
+static int af9015_eeprom_dump(struct dvb_usb_device *d)
+{
+	char buf[52], buf2[4];
+	u8 reg, val;
+
+	for (reg = 0; ; reg++) {
+		if (reg % 16 == 0) {
+			if (reg)
+				deb_info("%s\n", buf);
+			sprintf(buf, "%02x: ", reg);
+		}
+		if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0)
+			sprintf(buf2, "%02x ", val);
+		else
+			strcpy(buf2, "-- ");
+		strcat(buf, buf2);
+		if (reg == 0xff)
+			break;
+	}
+	deb_info("%s\n", buf);
+	return 0;
+}
+
+int af9015_download_ir_table(struct dvb_usb_device *d)
+{
+	int i, packets = 0, ret;
+	u16 addr = 0x9a56; /* ir-table start address */
+	struct req_t req = {WRITE_MEMORY, 0, 0, 0, 0, 1, NULL};
+	u8 *data = NULL;
+	deb_info("%s:\n", __func__);
+
+	data = af9015_config.ir_table;
+	packets = af9015_config.ir_table_size;
+
+	/* no remote */
+	if (!packets)
+		goto exit;
+
+	/* load remote ir-table */
+	for (i = 0; i < packets; i++) {
+		req.addr = addr + i;
+		req.data = &data[i];
+		ret = af9015_ctrl_msg(d, &req);
+		if (ret) {
+			err("ir-table download failed at packet %d with " \
+				"code %d", i, ret);
+			return ret;
+		}
+	}
+
+exit:
+	return 0;
+}
+
+static int af9015_init(struct dvb_usb_device *d)
+{
+	int ret;
+	deb_info("%s:\n", __func__);
+
+	ret = af9015_init_endpoint(d);
+	if (ret)
+		goto error;
+
+	ret = af9015_download_ir_table(d);
+	if (ret)
+		goto error;
+
+error:
+	return ret;
+}
+
+static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+	int ret;
+	deb_info("%s: onoff:%d\n", __func__, onoff);
+
+	if (onoff)
+		ret = af9015_set_reg_bit(adap->dev, 0xd503, 0);
+	else
+		ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0);
+
+	return ret;
+}
+
+static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
+	int onoff)
+{
+	int ret;
+	u8 idx;
+
+	deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n",
+		__func__, index, pid, onoff);
+
+	ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff));
+	if (ret)
+		goto error;
+
+	ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8));
+	if (ret)
+		goto error;
+
+	idx = ((index & 0x1f) | (1 << 5));
+	ret = af9015_write_reg(adap->dev, 0xd504, idx);
+
+error:
+	return ret;
+}
+
+static int af9015_download_firmware(struct usb_device *udev,
+	const struct firmware *fw)
+{
+	int i, len, packets, remainder, ret;
+	struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL};
+	u16 addr = 0x5100; /* firmware start address */
+	u16 checksum = 0;
+
+	deb_info("%s:\n", __func__);
+
+	/* calc checksum */
+	for (i = 0; i < fw->size; i++)
+		checksum += fw->data[i];
+
+	af9015_config.firmware_size = fw->size;
+	af9015_config.firmware_checksum = checksum;
+
+	#define FW_PACKET_MAX_DATA  55
+
+	packets = fw->size / FW_PACKET_MAX_DATA;
+	remainder = fw->size % FW_PACKET_MAX_DATA;
+	len = FW_PACKET_MAX_DATA;
+	for (i = 0; i <= packets; i++) {
+		if (i == packets)  /* set size of the last packet */
+			len = remainder;
+
+		req.data_len = len;
+		req.data = (fw->data + i * FW_PACKET_MAX_DATA);
+		req.addr = addr;
+		addr += FW_PACKET_MAX_DATA;
+
+		ret = af9015_rw_udev(udev, &req);
+		if (ret) {
+			err("firmware download failed at packet %d with " \
+				"code %d", i, ret);
+			goto error;
+		}
+	}
+	#undef FW_PACKET_MAX_DATA
+
+	/* firmware loaded, request boot */
+	req.cmd = BOOT;
+	ret = af9015_rw_udev(udev, &req);
+	if (ret) {
+		err("firmware boot failed:%d", ret);
+		goto error;
+	}
+
+	/* firmware is running, reconnect device in the usb bus */
+	req.cmd = RECONNECT_USB;
+	ret = af9015_rw_udev(udev, &req);
+	if (ret)
+		err("reconnect failed: %d", ret);
+
+error:
+	return ret;
+}
+
+static int af9015_read_config(struct usb_device *udev)
+{
+	int ret;
+	u8 val, i, offset = 0;
+	struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
+	char manufacturer[10];
+
+	/* IR remote controller */
+	req.addr = AF9015_EEPROM_IR_MODE;
+	ret = af9015_rw_udev(udev, &req);
+	if (ret)
+		goto error;
+	deb_info("%s: IR mode:%d\n", __func__, val);
+	for (i = 0; i < af9015_properties_count; i++) {
+		if (val == AF9015_IR_MODE_DISABLED || val == 0x04) {
+			af9015_properties[i].rc_key_map = NULL;
+			af9015_properties[i].rc_key_map_size  = 0;
+		} else if (dvb_usb_af9015_remote) {
+			/* load remote defined as module param */
+			switch (dvb_usb_af9015_remote) {
+			case AF9015_REMOTE_A_LINK_DTU_M:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_a_link;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_a_link);
+				af9015_config.ir_table = af9015_ir_table_a_link;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_a_link);
+				break;
+			case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_msi;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_msi);
+				af9015_config.ir_table = af9015_ir_table_msi;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_msi);
+				break;
+			case AF9015_REMOTE_MYGICTV_U718:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_mygictv;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_mygictv);
+				af9015_config.ir_table =
+				  af9015_ir_table_mygictv;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_mygictv);
+				break;
+			}
+		} else {
+			switch (udev->descriptor.idVendor) {
+			case cpu_to_le16(USB_VID_LEADTEK):
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_leadtek;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_leadtek);
+				af9015_config.ir_table =
+				  af9015_ir_table_leadtek;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_leadtek);
+				break;
+			case cpu_to_le16(USB_VID_VISIONPLUS):
+				if (udev->descriptor.idProduct ==
+				cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) {
+					af9015_properties[i].rc_key_map =
+					  af9015_rc_keys_twinhan;
+					af9015_properties[i].rc_key_map_size =
+					  ARRAY_SIZE(af9015_rc_keys_twinhan);
+					af9015_config.ir_table =
+					  af9015_ir_table_twinhan;
+					af9015_config.ir_table_size =
+					  ARRAY_SIZE(af9015_ir_table_twinhan);
+				}
+				break;
+			case cpu_to_le16(USB_VID_KWORLD_2):
+				/* TODO: use correct rc keys */
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_twinhan;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_twinhan);
+				af9015_config.ir_table = af9015_ir_table_kworld;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_kworld);
+				break;
+			/* Check USB manufacturer and product strings and try
+			   to determine correct remote in case of chip vendor
+			   reference IDs are used. */
+			case cpu_to_le16(USB_VID_AFATECH):
+				memset(manufacturer, 0, sizeof(manufacturer));
+				usb_string(udev, udev->descriptor.iManufacturer,
+					manufacturer, sizeof(manufacturer));
+				if (!strcmp("Geniatech", manufacturer)) {
+					/* iManufacturer 1 Geniatech
+					   iProduct      2 AF9015 */
+					af9015_properties[i].rc_key_map =
+					  af9015_rc_keys_mygictv;
+					af9015_properties[i].rc_key_map_size =
+					  ARRAY_SIZE(af9015_rc_keys_mygictv);
+					af9015_config.ir_table =
+					  af9015_ir_table_mygictv;
+					af9015_config.ir_table_size =
+					  ARRAY_SIZE(af9015_ir_table_mygictv);
+				} else if (!strcmp("MSI", manufacturer)) {
+					/* iManufacturer 1 MSI
+					   iProduct      2 MSI K-VOX */
+					af9015_properties[i].rc_key_map =
+					  af9015_rc_keys_msi;
+					af9015_properties[i].rc_key_map_size =
+					  ARRAY_SIZE(af9015_rc_keys_msi);
+					af9015_config.ir_table =
+					  af9015_ir_table_msi;
+					af9015_config.ir_table_size =
+					  ARRAY_SIZE(af9015_ir_table_msi);
+				}
+				break;
+			}
+		}
+	}
+
+	/* TS mode - one or two receivers */
+	req.addr = AF9015_EEPROM_TS_MODE;
+	ret = af9015_rw_udev(udev, &req);
+	if (ret)
+		goto error;
+	af9015_config.dual_mode = val;
+	deb_info("%s: TS mode:%d\n", __func__, af9015_config.dual_mode);
+	/* disable dual mode by default because it is buggy */
+	if (!dvb_usb_af9015_dual_mode)
+		af9015_config.dual_mode = 0;
+
+	/* set buffer size according to USB port speed */
+	for (i = 0; i < af9015_properties_count; i++) {
+		/* USB1.1 set smaller buffersize and disable 2nd adapter */
+		if (udev->speed == USB_SPEED_FULL) {
+			af9015_properties[i].adapter->stream.u.bulk.buffersize =
+				TS_USB11_MAX_PACKET_SIZE;
+			/* disable 2nd adapter because we don't have
+			   PID-filters */
+			af9015_config.dual_mode = 0;
+		} else {
+			af9015_properties[i].adapter->stream.u.bulk.buffersize =
+				TS_USB20_MAX_PACKET_SIZE;
+		}
+	}
+
+	if (af9015_config.dual_mode) {
+		/* read 2nd demodulator I2C address */
+		req.addr = AF9015_EEPROM_DEMOD2_I2C;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_af9013_config[1].demod_address = val;
+
+		/* enable 2nd adapter */
+		for (i = 0; i < af9015_properties_count; i++)
+			af9015_properties[i].num_adapters = 2;
+
+	} else {
+		 /* disable 2nd adapter */
+		for (i = 0; i < af9015_properties_count; i++)
+			af9015_properties[i].num_adapters = 1;
+	}
+
+	for (i = 0; i < af9015_properties[0].num_adapters; i++) {
+		if (i == 1)
+			offset = AF9015_EEPROM_OFFSET;
+		/* xtal */
+		req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		switch (val) {
+		case 0:
+			af9015_af9013_config[i].adc_clock = 28800;
+			break;
+		case 1:
+			af9015_af9013_config[i].adc_clock = 20480;
+			break;
+		case 2:
+			af9015_af9013_config[i].adc_clock = 28000;
+			break;
+		case 3:
+			af9015_af9013_config[i].adc_clock = 25000;
+			break;
+		};
+		deb_info("%s: [%d] xtal:%d set adc_clock:%d\n", __func__, i,
+			val, af9015_af9013_config[i].adc_clock);
+
+		/* tuner IF */
+		req.addr = AF9015_EEPROM_IF1H + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_af9013_config[i].tuner_if = val << 8;
+		req.addr = AF9015_EEPROM_IF1L + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_af9013_config[i].tuner_if += val;
+		deb_info("%s: [%d] IF1:%d\n", __func__, i,
+			af9015_af9013_config[0].tuner_if);
+
+		/* MT2060 IF1 */
+		req.addr = AF9015_EEPROM_MT2060_IF1H  + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_config.mt2060_if1[i] = val << 8;
+		req.addr = AF9015_EEPROM_MT2060_IF1L + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_config.mt2060_if1[i] += val;
+		deb_info("%s: [%d] MT2060 IF1:%d\n", __func__, i,
+			af9015_config.mt2060_if1[i]);
+
+		/* tuner */
+		req.addr =  AF9015_EEPROM_TUNER_ID1 + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		switch (val) {
+		case AF9013_TUNER_ENV77H11D5:
+		case AF9013_TUNER_MT2060:
+		case AF9013_TUNER_MC44S803:
+		case AF9013_TUNER_QT1010:
+		case AF9013_TUNER_UNKNOWN:
+		case AF9013_TUNER_MT2060_2:
+		case AF9013_TUNER_TDA18271:
+		case AF9013_TUNER_QT1010A:
+			af9015_af9013_config[i].rf_spec_inv = 1;
+			break;
+		case AF9013_TUNER_MXL5003D:
+		case AF9013_TUNER_MXL5005D:
+		case AF9013_TUNER_MXL5005R:
+			af9015_af9013_config[i].rf_spec_inv = 0;
+			break;
+		default:
+			warn("tuner id:%d not supported, please report!", val);
+			return -ENODEV;
+		};
+
+		af9015_af9013_config[i].tuner = val;
+		deb_info("%s: [%d] tuner id:%d\n", __func__, i, val);
+	}
+
+error:
+	if (ret)
+		err("eeprom read failed:%d", ret);
+
+	return ret;
+}
+
+static int af9015_identify_state(struct usb_device *udev,
+				 struct dvb_usb_device_properties *props,
+				 struct dvb_usb_device_description **desc,
+				 int *cold)
+{
+	int ret;
+	u8 reply;
+	struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply};
+
+	ret = af9015_rw_udev(udev, &req);
+	if (ret)
+		return ret;
+
+	deb_info("%s: reply:%02x\n", __func__, reply);
+	if (reply == 0x02)
+		*cold = 0;
+	else
+		*cold = 1;
+
+	return ret;
+}
+
+static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+	u8 buf[8];
+	struct req_t req = {GET_IR_CODE, 0, 0, 0, 0, sizeof(buf), buf};
+	struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+	int i, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	ret = af9015_ctrl_msg(d, &req);
+	if (ret)
+		return ret;
+
+	*event = 0;
+	*state = REMOTE_NO_KEY_PRESSED;
+
+	for (i = 0; i < d->props.rc_key_map_size; i++) {
+		if (!buf[1] && keymap[i].custom == buf[0] &&
+		    keymap[i].data == buf[2]) {
+			*event = keymap[i].event;
+			*state = REMOTE_KEY_PRESSED;
+			break;
+		}
+	}
+	if (!buf[1])
+		deb_rc("%s: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+			__func__, buf[0], buf[1], buf[2], buf[3], buf[4],
+			buf[5], buf[6], buf[7]);
+
+	return 0;
+}
+
+/* init 2nd I2C adapter */
+int af9015_i2c_init(struct dvb_usb_device *d)
+{
+	int ret;
+	struct af9015_state *state = d->priv;
+	deb_info("%s:\n", __func__);
+
+	strncpy(state->i2c_adap.name, d->desc->name,
+		sizeof(state->i2c_adap.name));
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+	state->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
+#else
+	state->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
+#endif
+	state->i2c_adap.algo      = d->props.i2c_algo;
+	state->i2c_adap.algo_data = NULL;
+	state->i2c_adap.dev.parent = &d->udev->dev;
+
+	i2c_set_adapdata(&state->i2c_adap, d);
+
+	ret = i2c_add_adapter(&state->i2c_adap);
+	if (ret < 0)
+		err("could not add i2c adapter");
+
+	return ret;
+}
+
+static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	int ret;
+	struct af9015_state *state = adap->dev->priv;
+	struct i2c_adapter *i2c_adap;
+
+	if (adap->id == 0) {
+		/* select I2C adapter */
+		i2c_adap = &adap->dev->i2c_adap;
+
+		deb_info("%s: init I2C\n", __func__);
+		ret = af9015_i2c_init(adap->dev);
+
+		/* dump eeprom (debug) */
+		ret = af9015_eeprom_dump(adap->dev);
+		if (ret)
+			return ret;
+	} else {
+		/* select I2C adapter */
+		i2c_adap = &state->i2c_adap;
+
+		/* copy firmware to 2nd demodulator */
+		if (af9015_config.dual_mode) {
+			ret = af9015_copy_firmware(adap->dev);
+			if (ret) {
+				err("firmware copy to 2nd frontend " \
+					"failed, will disable it");
+				af9015_config.dual_mode = 0;
+				return -ENODEV;
+			}
+		} else {
+			return -ENODEV;
+		}
+	}
+
+	/* attach demodulator */
+	adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
+		i2c_adap);
+
+	return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static struct mt2060_config af9015_mt2060_config = {
+	.i2c_address = 0xc0,
+	.clock_out = 0,
+};
+
+static struct qt1010_config af9015_qt1010_config = {
+	.i2c_address = 0xc4,
+};
+
+static struct tda18271_config af9015_tda18271_config = {
+	.gate = TDA18271_GATE_DIGITAL,
+	.small_i2c = 1,
+};
+
+static struct mxl5005s_config af9015_mxl5003_config = {
+	.i2c_address     = 0xc6,
+	.if_freq         = IF_FREQ_4570000HZ,
+	.xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+	.agc_mode        = MXL_SINGLE_AGC,
+	.tracking_filter = MXL_TF_DEFAULT,
+	.rssi_enable     = MXL_RSSI_DISABLE,
+	.cap_select      = MXL_CAP_SEL_ENABLE,
+	.div_out         = MXL_DIV_OUT_4,
+	.clock_out       = MXL_CLOCK_OUT_DISABLE,
+	.output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+	.top		 = MXL5005S_TOP_25P2,
+	.mod_mode        = MXL_DIGITAL_MODE,
+	.if_mode         = MXL_ZERO_IF,
+	.AgcMasterByte   = 0x00,
+};
+
+static struct mxl5005s_config af9015_mxl5005_config = {
+	.i2c_address     = 0xc6,
+	.if_freq         = IF_FREQ_4570000HZ,
+	.xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+	.agc_mode        = MXL_SINGLE_AGC,
+	.tracking_filter = MXL_TF_OFF,
+	.rssi_enable     = MXL_RSSI_DISABLE,
+	.cap_select      = MXL_CAP_SEL_ENABLE,
+	.div_out         = MXL_DIV_OUT_4,
+	.clock_out       = MXL_CLOCK_OUT_DISABLE,
+	.output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+	.top		 = MXL5005S_TOP_25P2,
+	.mod_mode        = MXL_DIGITAL_MODE,
+	.if_mode         = MXL_ZERO_IF,
+	.AgcMasterByte   = 0x00,
+};
+
+static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	struct af9015_state *state = adap->dev->priv;
+	struct i2c_adapter *i2c_adap;
+	int ret;
+	deb_info("%s: \n", __func__);
+
+	/* select I2C adapter */
+	if (adap->id == 0)
+		i2c_adap = &adap->dev->i2c_adap;
+	else
+		i2c_adap = &state->i2c_adap;
+
+	switch (af9015_af9013_config[adap->id].tuner) {
+	case AF9013_TUNER_MT2060:
+	case AF9013_TUNER_MT2060_2:
+		ret = dvb_attach(mt2060_attach, adap->fe, i2c_adap,
+			&af9015_mt2060_config,
+			af9015_config.mt2060_if1[adap->id])
+			== NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_QT1010:
+	case AF9013_TUNER_QT1010A:
+		ret = dvb_attach(qt1010_attach, adap->fe, i2c_adap,
+			&af9015_qt1010_config) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_TDA18271:
+		ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap,
+			&af9015_tda18271_config) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_MXL5003D:
+		ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+			&af9015_mxl5003_config) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_MXL5005D:
+	case AF9013_TUNER_MXL5005R:
+		ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+			&af9015_mxl5005_config) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_ENV77H11D5:
+		ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, i2c_adap,
+			DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_MC44S803:
+#if 0
+		ret = dvb_attach(mc44s80x_attach, adap->fe, i2c_adap)
+			== NULL ? -ENODEV : 0;
+#else
+		ret = -ENODEV;
+		info("Freescale MC44S803 tuner found but no driver for that" \
+			"tuner. Look at the Linuxtv.org for tuner driver" \
+			"status.");
+#endif
+		break;
+	case AF9013_TUNER_UNKNOWN:
+	default:
+		ret = -ENODEV;
+		err("Unknown tuner id:%d",
+			af9015_af9013_config[adap->id].tuner);
+	}
+	return ret;
+}
+
+static struct usb_device_id af9015_usb_table[] = {
+/*  0 */{USB_DEVICE(USB_VID_AFATECH,   USB_PID_AFATECH_AF9015_9015)},
+	{USB_DEVICE(USB_VID_AFATECH,   USB_PID_AFATECH_AF9015_9016)},
+	{USB_DEVICE(USB_VID_LEADTEK,   USB_PID_WINFAST_DTV_DONGLE_GOLD)},
+	{USB_DEVICE(USB_VID_PINNACLE,  USB_PID_PINNACLE_PCTV71E)},
+	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_399U)},
+/*  5 */{USB_DEVICE(USB_VID_VISIONPLUS,
+		USB_PID_TINYTWIN)},
+	{USB_DEVICE(USB_VID_VISIONPLUS,
+		USB_PID_AZUREWAVE_AD_TU700)},
+	{USB_DEVICE(USB_VID_TERRATEC,  USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)},
+	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_PC160_2T)},
+	{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)},
+/* 10 */{USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)},
+	{USB_DEVICE(USB_VID_MSI_2,     USB_PID_MSI_DIGIVOX_DUO)},
+	{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)},
+	{0},
+};
+MODULE_DEVICE_TABLE(usb, af9015_usb_table);
+
+static struct dvb_usb_device_properties af9015_properties[] = {
+	{
+		.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+		.usb_ctrl = DEVICE_SPECIFIC,
+		.download_firmware = af9015_download_firmware,
+		.firmware = "dvb-usb-af9015.fw",
+
+		.size_of_priv = sizeof(struct af9015_state), \
+
+		.num_adapters = 2,
+		.adapter = {
+			{
+				.caps = DVB_USB_ADAP_HAS_PID_FILTER |
+				DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+				.pid_filter_count = 32,
+				.pid_filter       = af9015_pid_filter,
+				.pid_filter_ctrl  = af9015_pid_filter_ctrl,
+
+				.frontend_attach =
+					af9015_af9013_frontend_attach,
+				.tuner_attach    = af9015_tuner_attach,
+				.stream = {
+					.type = USB_BULK,
+					.count = 6,
+					.endpoint = 0x84,
+				},
+			},
+			{
+				.frontend_attach =
+					af9015_af9013_frontend_attach,
+				.tuner_attach    = af9015_tuner_attach,
+				.stream = {
+					.type = USB_BULK,
+					.count = 6,
+					.endpoint = 0x85,
+				},
+			}
+		},
+
+		.identify_state = af9015_identify_state,
+
+		.rc_query         = af9015_rc_query,
+		.rc_interval      = 150,
+
+		.i2c_algo = &af9015_i2c_algo,
+
+		.num_device_descs = 9,
+		.devices = {
+			{
+				.name = "Afatech AF9015 DVB-T USB2.0 stick",
+				.cold_ids = {&af9015_usb_table[0],
+					     &af9015_usb_table[1], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "Leadtek WinFast DTV Dongle Gold",
+				.cold_ids = {&af9015_usb_table[2], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "Pinnacle PCTV 71e",
+				.cold_ids = {&af9015_usb_table[3], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "KWorld PlusTV Dual DVB-T Stick " \
+					"(DVB-T 399U)",
+				.cold_ids = {&af9015_usb_table[4], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "DigitalNow TinyTwin DVB-T Receiver",
+				.cold_ids = {&af9015_usb_table[5], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "TwinHan AzureWave AD-TU700(704J)",
+				.cold_ids = {&af9015_usb_table[6], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "TerraTec Cinergy T USB XE",
+				.cold_ids = {&af9015_usb_table[7], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "KWorld PlusTV Dual DVB-T PCI " \
+					"(DVB-T PC160-2T)",
+				.cold_ids = {&af9015_usb_table[8], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "AVerMedia AVerTV DVB-T Volar X",
+				.cold_ids = {&af9015_usb_table[9], NULL},
+				.warm_ids = {NULL},
+			},
+		}
+	}, {
+		.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+		.usb_ctrl = DEVICE_SPECIFIC,
+		.download_firmware = af9015_download_firmware,
+		.firmware = "dvb-usb-af9015.fw",
+
+		.size_of_priv = sizeof(struct af9015_state), \
+
+		.num_adapters = 2,
+		.adapter = {
+			{
+				.caps = DVB_USB_ADAP_HAS_PID_FILTER |
+				DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+				.pid_filter_count = 32,
+				.pid_filter       = af9015_pid_filter,
+				.pid_filter_ctrl  = af9015_pid_filter_ctrl,
+
+				.frontend_attach =
+					af9015_af9013_frontend_attach,
+				.tuner_attach    = af9015_tuner_attach,
+				.stream = {
+					.type = USB_BULK,
+					.count = 6,
+					.endpoint = 0x84,
+				},
+			},
+			{
+				.frontend_attach =
+					af9015_af9013_frontend_attach,
+				.tuner_attach    = af9015_tuner_attach,
+				.stream = {
+					.type = USB_BULK,
+					.count = 6,
+					.endpoint = 0x85,
+				},
+			}
+		},
+
+		.identify_state = af9015_identify_state,
+
+		.rc_query         = af9015_rc_query,
+		.rc_interval      = 150,
+
+		.i2c_algo = &af9015_i2c_algo,
+
+		.num_device_descs = 3,
+		.devices = {
+			{
+				.name = "Xtensions XD-380",
+				.cold_ids = {&af9015_usb_table[10], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "MSI DIGIVOX Duo",
+				.cold_ids = {&af9015_usb_table[11], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "Fujitsu-Siemens Slim Mobile USB DVB-T",
+				.cold_ids = {&af9015_usb_table[12], NULL},
+				.warm_ids = {NULL},
+			},
+		}
+	}
+};
+#undef AF9015_DEFAULT_PROPERTIES
+
+static int af9015_usb_probe(struct usb_interface *intf,
+			    const struct usb_device_id *id)
+{
+	int ret = 0;
+	struct dvb_usb_device *d = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	u8 i;
+
+	deb_info("%s: interface:%d\n", __func__,
+		intf->cur_altsetting->desc.bInterfaceNumber);
+
+	/* interface 0 is used by DVB-T receiver and
+	   interface 1 is for remote controller (HID) */
+	if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+		ret = af9015_read_config(udev);
+		if (ret)
+			return ret;
+
+		for (i = 0; i < af9015_properties_count; i++) {
+			ret = dvb_usb_device_init(intf, &af9015_properties[i],
+				THIS_MODULE, &d, adapter_nr);
+			if (!ret)
+				break;
+			if (ret != -ENODEV)
+				return ret;
+		}
+		if (ret)
+			return ret;
+
+		if (d)
+			ret = af9015_init(d);
+	}
+
+	return ret;
+}
+
+void af9015_i2c_exit(struct dvb_usb_device *d)
+{
+	struct af9015_state *state = d->priv;
+	deb_info("%s: \n", __func__);
+
+	/* remove 2nd I2C adapter */
+	if (d->state & DVB_USB_STATE_I2C)
+		i2c_del_adapter(&state->i2c_adap);
+}
+
+static void af9015_usb_device_exit(struct usb_interface *intf)
+{
+	struct dvb_usb_device *d = usb_get_intfdata(intf);
+	deb_info("%s: \n", __func__);
+
+	/* remove 2nd I2C adapter */
+	if (d != NULL && d->desc != NULL)
+		af9015_i2c_exit(d);
+
+	dvb_usb_device_exit(intf);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver af9015_usb_driver = {
+	.name = "dvb_usb_af9015",
+	.probe = af9015_usb_probe,
+	.disconnect = af9015_usb_device_exit,
+	.id_table = af9015_usb_table,
+};
+
+/* module stuff */
+static int __init af9015_usb_module_init(void)
+{
+	int ret;
+	ret = usb_register(&af9015_usb_driver);
+	if (ret)
+		err("module init failed:%d", ret);
+
+	return ret;
+}
+
+static void __exit af9015_usb_module_exit(void)
+{
+	/* deregister this driver from the USB subsystem */
+	usb_deregister(&af9015_usb_driver);
+}
+
+module_init(af9015_usb_module_init);
+module_exit(af9015_usb_module_exit);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
new file mode 100644
index 0000000..882e8a4
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -0,0 +1,524 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ *    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 _DVB_USB_AF9015_H_
+#define _DVB_USB_AF9015_H_
+
+#define DVB_USB_LOG_PREFIX "af9015"
+#include "dvb-usb.h"
+
+extern int dvb_usb_af9015_debug;
+#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args)
+#define deb_rc(args...)   dprintk(dvb_usb_af9015_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args)
+#define deb_reg(args...)  dprintk(dvb_usb_af9015_debug, 0x08, args)
+#define deb_i2c(args...)  dprintk(dvb_usb_af9015_debug, 0x10, args)
+#define deb_fw(args...)   dprintk(dvb_usb_af9015_debug, 0x20, args)
+
+#define AF9015_I2C_EEPROM  0xa0
+#define AF9015_I2C_DEMOD   0x38
+#define AF9015_USB_TIMEOUT 2000
+
+/* EEPROM locations */
+#define AF9015_EEPROM_IR_MODE        0x18
+#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34
+#define AF9015_EEPROM_TS_MODE        0x31
+#define AF9015_EEPROM_DEMOD2_I2C     0x32
+
+#define AF9015_EEPROM_SAW_BW1        0x35
+#define AF9015_EEPROM_XTAL_TYPE1     0x36
+#define AF9015_EEPROM_SPEC_INV1      0x37
+#define AF9015_EEPROM_IF1L           0x38
+#define AF9015_EEPROM_IF1H           0x39
+#define AF9015_EEPROM_MT2060_IF1L    0x3a
+#define AF9015_EEPROM_MT2060_IF1H    0x3b
+#define AF9015_EEPROM_TUNER_ID1      0x3c
+
+#define AF9015_EEPROM_SAW_BW2        0x45
+#define AF9015_EEPROM_XTAL_TYPE2     0x46
+#define AF9015_EEPROM_SPEC_INV2      0x47
+#define AF9015_EEPROM_IF2L           0x48
+#define AF9015_EEPROM_IF2H           0x49
+#define AF9015_EEPROM_MT2060_IF2L    0x4a
+#define AF9015_EEPROM_MT2060_IF2H    0x4b
+#define AF9015_EEPROM_TUNER_ID2      0x4c
+
+#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1)
+
+#define AF9015_GPIO_ON (1 << 0)
+#define AF9015_GPIO_EN (1 << 1)
+#define AF9015_GPIO_O  (1 << 2)
+#define AF9015_GPIO_I  (1 << 3)
+
+#define AF9015_GPIO_TUNER_ON  (AF9015_GPIO_ON|AF9015_GPIO_EN)
+#define AF9015_GPIO_TUNER_OFF (AF9015_GPIO_ON|AF9015_GPIO_EN|AF9015_GPIO_O)
+
+struct req_t {
+	u8  cmd;       /* [0] */
+	/*  seq */     /* [1] */
+	u8  i2c_addr;  /* [2] */
+	u16 addr;      /* [3|4] */
+	u8  mbox;      /* [5] */
+	u8  addr_len;  /* [6] */
+	u8  data_len;  /* [7] */
+	u8  *data;
+};
+
+enum af9015_cmd {
+	GET_CONFIG           = 0x10,
+	DOWNLOAD_FIRMWARE    = 0x11,
+	BOOT                 = 0x13,
+	READ_MEMORY          = 0x20,
+	WRITE_MEMORY         = 0x21,
+	READ_WRITE_I2C       = 0x22,
+	COPY_FIRMWARE        = 0x23,
+	RECONNECT_USB        = 0x5a,
+	WRITE_VIRTUAL_MEMORY = 0x26,
+	GET_IR_CODE          = 0x27,
+	READ_I2C,
+	WRITE_I2C,
+};
+
+enum af9015_ir_mode {
+	AF9015_IR_MODE_DISABLED = 0,
+	AF9015_IR_MODE_HID,
+	AF9015_IR_MODE_RLC,
+	AF9015_IR_MODE_RC6,
+};
+
+struct af9015_state {
+	struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */
+};
+
+struct af9015_config {
+	u8  dual_mode:1;
+	u16 mt2060_if1[2];
+	u16 firmware_size;
+	u16 firmware_checksum;
+	u8  *ir_table;
+	u16 ir_table_size;
+};
+
+enum af9015_remote {
+	AF9015_REMOTE_NONE                    = 0,
+	AF9015_REMOTE_A_LINK_DTU_M,
+	AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+	AF9015_REMOTE_MYGICTV_U718,
+};
+
+/* Leadtek WinFast DTV Dongle Gold */
+static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = {
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x00, 0x28, KEY_ENTER },
+	{ 0x00, 0x4f, KEY_VOLUMEUP },
+	{ 0x00, 0x50, KEY_VOLUMEDOWN },
+	{ 0x00, 0x51, KEY_CHANNELDOWN },
+	{ 0x00, 0x52, KEY_CHANNELUP },
+};
+
+static u8 af9015_ir_table_leadtek[] = {
+	0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00,
+	0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00,
+	0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00,
+	0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00,
+	0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00,
+	0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00,
+	0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00,
+	0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00,
+	0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00,
+	0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00,
+	0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00,
+	0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00,
+	0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00,
+	0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00,
+	0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00,
+	0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00,
+	0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00,
+	0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00,
+	0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00,
+	0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00,
+	0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00,
+	0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00,
+	0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00,
+	0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00,
+	0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00,
+	0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00,
+	0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00,
+	0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00,
+	0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00,
+	0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00,
+	0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+	0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00,
+	0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00,
+	0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00,
+	0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00,
+	0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00,
+	0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00,
+	0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00,
+	0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00,
+	0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00,
+	0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00,
+	0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00,
+	0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00,
+	0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00,
+	0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00,
+};
+
+/* TwinHan AzureWave AD-TU700(704J) */
+static struct dvb_usb_rc_key af9015_rc_keys_twinhan[] = {
+	{ 0x05, 0x3f, KEY_POWER },
+	{ 0x00, 0x19, KEY_FAVORITES },    /* Favorite List */
+	{ 0x00, 0x04, KEY_TEXT },         /* Teletext */
+	{ 0x00, 0x0e, KEY_POWER },
+	{ 0x00, 0x0e, KEY_INFO },         /* Preview */
+	{ 0x00, 0x08, KEY_EPG },          /* Info/EPG */
+	{ 0x00, 0x0f, KEY_LIST },         /* Record List */
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x00, 0x29, KEY_CANCEL },       /* Cancel */
+	{ 0x00, 0x4c, KEY_CLEAR },        /* Clear */
+	{ 0x00, 0x2a, KEY_BACK },         /* Back */
+	{ 0x00, 0x2b, KEY_TAB },          /* Tab */
+	{ 0x00, 0x52, KEY_UP },           /* up arrow */
+	{ 0x00, 0x51, KEY_DOWN },         /* down arrow */
+	{ 0x00, 0x4f, KEY_RIGHT },        /* right arrow */
+	{ 0x00, 0x50, KEY_LEFT },         /* left arrow */
+	{ 0x00, 0x28, KEY_ENTER },        /* Enter / ok */
+	{ 0x02, 0x52, KEY_VOLUMEUP },
+	{ 0x02, 0x51, KEY_VOLUMEDOWN },
+	{ 0x00, 0x4e, KEY_CHANNELDOWN },
+	{ 0x00, 0x4b, KEY_CHANNELUP },
+	{ 0x00, 0x4a, KEY_RECORD },
+	{ 0x01, 0x11, KEY_PLAY },
+	{ 0x00, 0x17, KEY_PAUSE },
+	{ 0x00, 0x0c, KEY_REWIND },       /* FR << */
+	{ 0x00, 0x11, KEY_FASTFORWARD },  /* FF >> */
+	{ 0x01, 0x15, KEY_PREVIOUS },     /* Replay */
+	{ 0x01, 0x0e, KEY_NEXT },         /* Skip */
+	{ 0x00, 0x13, KEY_CAMERA },       /* Capture */
+	{ 0x01, 0x0f, KEY_LANGUAGE },     /* SAP */
+	{ 0x01, 0x13, KEY_TV2 },          /* PIP */
+	{ 0x00, 0x1d, KEY_ZOOM },         /* Full Screen */
+	{ 0x01, 0x17, KEY_SUBTITLE },     /* Subtitle / CC */
+	{ 0x00, 0x10, KEY_MUTE },
+	{ 0x01, 0x19, KEY_AUDIO },        /* L/R */ /* TODO better event */
+	{ 0x01, 0x16, KEY_SLEEP },        /* Hibernate */
+	{ 0x01, 0x16, KEY_SWITCHVIDEOMODE },
+					  /* A/V */ /* TODO does not work */
+	{ 0x00, 0x06, KEY_AGAIN },        /* Recall */
+	{ 0x01, 0x16, KEY_KPPLUS },       /* Zoom+ */ /* TODO does not work */
+	{ 0x01, 0x16, KEY_KPMINUS },      /* Zoom- */ /* TODO does not work */
+	{ 0x02, 0x15, KEY_RED },
+	{ 0x02, 0x0a, KEY_GREEN },
+	{ 0x02, 0x1c, KEY_YELLOW },
+	{ 0x02, 0x05, KEY_BLUE },
+};
+
+static u8 af9015_ir_table_twinhan[] = {
+	0x00, 0xff, 0x16, 0xe9, 0x3f, 0x05, 0x00,
+	0x00, 0xff, 0x07, 0xf8, 0x16, 0x01, 0x00,
+	0x00, 0xff, 0x14, 0xeb, 0x11, 0x01, 0x00,
+	0x00, 0xff, 0x1a, 0xe5, 0x4d, 0x00, 0x00,
+	0x00, 0xff, 0x4c, 0xb3, 0x17, 0x00, 0x00,
+	0x00, 0xff, 0x12, 0xed, 0x11, 0x00, 0x00,
+	0x00, 0xff, 0x40, 0xbf, 0x0c, 0x00, 0x00,
+	0x00, 0xff, 0x11, 0xee, 0x4a, 0x00, 0x00,
+	0x00, 0xff, 0x54, 0xab, 0x13, 0x00, 0x00,
+	0x00, 0xff, 0x41, 0xbe, 0x15, 0x01, 0x00,
+	0x00, 0xff, 0x42, 0xbd, 0x0e, 0x01, 0x00,
+	0x00, 0xff, 0x43, 0xbc, 0x17, 0x01, 0x00,
+	0x00, 0xff, 0x50, 0xaf, 0x0f, 0x01, 0x00,
+	0x00, 0xff, 0x4d, 0xb2, 0x1d, 0x00, 0x00,
+	0x00, 0xff, 0x47, 0xb8, 0x13, 0x01, 0x00,
+	0x00, 0xff, 0x05, 0xfa, 0x4b, 0x00, 0x00,
+	0x00, 0xff, 0x02, 0xfd, 0x4e, 0x00, 0x00,
+	0x00, 0xff, 0x0e, 0xf1, 0x06, 0x00, 0x00,
+	0x00, 0xff, 0x1e, 0xe1, 0x52, 0x02, 0x00,
+	0x00, 0xff, 0x0a, 0xf5, 0x51, 0x02, 0x00,
+	0x00, 0xff, 0x10, 0xef, 0x10, 0x00, 0x00,
+	0x00, 0xff, 0x49, 0xb6, 0x19, 0x01, 0x00,
+	0x00, 0xff, 0x15, 0xea, 0x27, 0x00, 0x00,
+	0x00, 0xff, 0x03, 0xfc, 0x1e, 0x00, 0x00,
+	0x00, 0xff, 0x01, 0xfe, 0x1f, 0x00, 0x00,
+	0x00, 0xff, 0x06, 0xf9, 0x20, 0x00, 0x00,
+	0x00, 0xff, 0x09, 0xf6, 0x21, 0x00, 0x00,
+	0x00, 0xff, 0x1d, 0xe2, 0x22, 0x00, 0x00,
+	0x00, 0xff, 0x1f, 0xe0, 0x23, 0x00, 0x00,
+	0x00, 0xff, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+	0x00, 0xff, 0x19, 0xe6, 0x25, 0x00, 0x00,
+	0x00, 0xff, 0x1b, 0xe4, 0x26, 0x00, 0x00,
+	0x00, 0xff, 0x00, 0xff, 0x2b, 0x00, 0x00,
+	0x00, 0xff, 0x4a, 0xb5, 0x4c, 0x00, 0x00,
+	0x00, 0xff, 0x4b, 0xb4, 0x52, 0x00, 0x00,
+	0x00, 0xff, 0x51, 0xae, 0x51, 0x00, 0x00,
+	0x00, 0xff, 0x52, 0xad, 0x4f, 0x00, 0x00,
+	0x00, 0xff, 0x4e, 0xb1, 0x50, 0x00, 0x00,
+	0x00, 0xff, 0x0c, 0xf3, 0x29, 0x00, 0x00,
+	0x00, 0xff, 0x4f, 0xb0, 0x28, 0x00, 0x00,
+	0x00, 0xff, 0x13, 0xec, 0x2a, 0x00, 0x00,
+	0x00, 0xff, 0x17, 0xe8, 0x19, 0x00, 0x00,
+	0x00, 0xff, 0x04, 0xfb, 0x0f, 0x00, 0x00,
+	0x00, 0xff, 0x48, 0xb7, 0x0e, 0x00, 0x00,
+	0x00, 0xff, 0x0f, 0xf0, 0x04, 0x00, 0x00,
+	0x00, 0xff, 0x1c, 0xe3, 0x08, 0x00, 0x00,
+	0x00, 0xff, 0x18, 0xe7, 0x15, 0x02, 0x00,
+	0x00, 0xff, 0x53, 0xac, 0x0a, 0x02, 0x00,
+	0x00, 0xff, 0x5e, 0xa1, 0x1c, 0x02, 0x00,
+	0x00, 0xff, 0x5f, 0xa0, 0x05, 0x02, 0x00,
+};
+
+/* A-Link DTU(m) */
+static struct dvb_usb_rc_key af9015_rc_keys_a_link[] = {
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x00, 0x2e, KEY_CHANNELUP },
+	{ 0x00, 0x2d, KEY_CHANNELDOWN },
+	{ 0x04, 0x28, KEY_ZOOM },
+	{ 0x00, 0x41, KEY_MUTE },
+	{ 0x00, 0x42, KEY_VOLUMEDOWN },
+	{ 0x00, 0x43, KEY_VOLUMEUP },
+	{ 0x00, 0x44, KEY_GOTO },         /* jump */
+	{ 0x05, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_a_link[] = {
+	0x08, 0xf7, 0x12, 0xed, 0x45, 0x05, 0x00, /* power */
+	0x08, 0xf7, 0x1a, 0xe5, 0x41, 0x00, 0x00, /* mute */
+	0x08, 0xf7, 0x01, 0xfe, 0x1e, 0x00, 0x00, /* 1 */
+	0x08, 0xf7, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+	0x08, 0xf7, 0x03, 0xfc, 0x24, 0x00, 0x00, /* 7 */
+	0x08, 0xf7, 0x05, 0xfa, 0x28, 0x04, 0x00, /* zoom */
+	0x08, 0xf7, 0x00, 0xff, 0x43, 0x00, 0x00, /* volume up */
+	0x08, 0xf7, 0x16, 0xe9, 0x42, 0x00, 0x00, /* volume down */
+	0x08, 0xf7, 0x0f, 0xf0, 0x1f, 0x00, 0x00, /* 2 */
+	0x08, 0xf7, 0x0d, 0xf2, 0x22, 0x00, 0x00, /* 5 */
+	0x08, 0xf7, 0x1b, 0xe4, 0x25, 0x00, 0x00, /* 8 */
+	0x08, 0xf7, 0x06, 0xf9, 0x27, 0x00, 0x00, /* 0 */
+	0x08, 0xf7, 0x14, 0xeb, 0x2e, 0x00, 0x00, /* channel up */
+	0x08, 0xf7, 0x1d, 0xe2, 0x2d, 0x00, 0x00, /* channel down */
+	0x08, 0xf7, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+	0x08, 0xf7, 0x18, 0xe7, 0x23, 0x00, 0x00, /* 6 */
+	0x08, 0xf7, 0x04, 0xfb, 0x26, 0x00, 0x00, /* 9 */
+	0x08, 0xf7, 0x07, 0xf8, 0x44, 0x00, 0x00, /* jump */
+};
+
+/* MSI DIGIVOX mini II V3.0 */
+static struct dvb_usb_rc_key af9015_rc_keys_msi[] = {
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x03, 0x0f, KEY_CHANNELUP },
+	{ 0x03, 0x0e, KEY_CHANNELDOWN },
+	{ 0x00, 0x42, KEY_VOLUMEDOWN },
+	{ 0x00, 0x43, KEY_VOLUMEUP },
+	{ 0x05, 0x45, KEY_POWER },
+	{ 0x00, 0x52, KEY_UP },           /* up */
+	{ 0x00, 0x51, KEY_DOWN },         /* down */
+	{ 0x00, 0x28, KEY_ENTER },
+};
+
+static u8 af9015_ir_table_msi[] = {
+	0x03, 0xfc, 0x17, 0xe8, 0x45, 0x05, 0x00, /* power */
+	0x03, 0xfc, 0x0d, 0xf2, 0x51, 0x00, 0x00, /* down */
+	0x03, 0xfc, 0x03, 0xfc, 0x52, 0x00, 0x00, /* up */
+	0x03, 0xfc, 0x1a, 0xe5, 0x1e, 0x00, 0x00, /* 1 */
+	0x03, 0xfc, 0x02, 0xfd, 0x1f, 0x00, 0x00, /* 2 */
+	0x03, 0xfc, 0x04, 0xfb, 0x20, 0x00, 0x00, /* 3 */
+	0x03, 0xfc, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+	0x03, 0xfc, 0x08, 0xf7, 0x22, 0x00, 0x00, /* 5 */
+	0x03, 0xfc, 0x1d, 0xe2, 0x23, 0x00, 0x00, /* 6 */
+	0x03, 0xfc, 0x11, 0xee, 0x24, 0x00, 0x00, /* 7 */
+	0x03, 0xfc, 0x0b, 0xf4, 0x25, 0x00, 0x00, /* 8 */
+	0x03, 0xfc, 0x10, 0xef, 0x26, 0x00, 0x00, /* 9 */
+	0x03, 0xfc, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+	0x03, 0xfc, 0x14, 0xeb, 0x43, 0x00, 0x00, /* volume up */
+	0x03, 0xfc, 0x1f, 0xe0, 0x42, 0x00, 0x00, /* volume down */
+	0x03, 0xfc, 0x15, 0xea, 0x0f, 0x03, 0x00, /* channel up */
+	0x03, 0xfc, 0x05, 0xfa, 0x0e, 0x03, 0x00, /* channel down */
+	0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* enter */
+};
+
+/* MYGICTV U718 */
+static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = {
+	{ 0x00, 0x3d, KEY_SWITCHVIDEOMODE },
+					  /* TV / AV */
+	{ 0x05, 0x45, KEY_POWER },
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x00, 0x41, KEY_MUTE },
+	{ 0x00, 0x2a, KEY_ESC },          /* Esc */
+	{ 0x00, 0x2e, KEY_CHANNELUP },
+	{ 0x00, 0x2d, KEY_CHANNELDOWN },
+	{ 0x00, 0x42, KEY_VOLUMEDOWN },
+	{ 0x00, 0x43, KEY_VOLUMEUP },
+	{ 0x00, 0x52, KEY_UP },           /* up arrow */
+	{ 0x00, 0x51, KEY_DOWN },         /* down arrow */
+	{ 0x00, 0x4f, KEY_RIGHT },        /* right arrow */
+	{ 0x00, 0x50, KEY_LEFT },         /* left arrow */
+	{ 0x00, 0x28, KEY_ENTER },        /* ok */
+	{ 0x01, 0x15, KEY_RECORD },
+	{ 0x03, 0x13, KEY_PLAY },
+	{ 0x01, 0x13, KEY_PAUSE },
+	{ 0x01, 0x16, KEY_STOP },
+	{ 0x03, 0x07, KEY_REWIND },       /* FR << */
+	{ 0x03, 0x09, KEY_FASTFORWARD },  /* FF >> */
+	{ 0x00, 0x3b, KEY_TIME },         /* TimeShift */
+	{ 0x00, 0x3e, KEY_CAMERA },       /* Snapshot */
+	{ 0x03, 0x16, KEY_CYCLEWINDOWS }, /* yellow, min / max */
+	{ 0x00, 0x00, KEY_ZOOM },         /* 'select' (?) */
+	{ 0x03, 0x16, KEY_SHUFFLE },      /* Shuffle */
+	{ 0x03, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_mygictv[] = {
+	0x02, 0xbd, 0x0c, 0xf3, 0x3d, 0x00, 0x00, /* TV / AV */
+	0x02, 0xbd, 0x14, 0xeb, 0x45, 0x05, 0x00, /* power */
+	0x02, 0xbd, 0x00, 0xff, 0x1e, 0x00, 0x00, /* 1 */
+	0x02, 0xbd, 0x01, 0xfe, 0x1f, 0x00, 0x00, /* 2 */
+	0x02, 0xbd, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+	0x02, 0xbd, 0x03, 0xfc, 0x21, 0x00, 0x00, /* 4 */
+	0x02, 0xbd, 0x04, 0xfb, 0x22, 0x00, 0x00, /* 5 */
+	0x02, 0xbd, 0x05, 0xfa, 0x23, 0x00, 0x00, /* 6 */
+	0x02, 0xbd, 0x06, 0xf9, 0x24, 0x00, 0x00, /* 7 */
+	0x02, 0xbd, 0x07, 0xf8, 0x25, 0x00, 0x00, /* 8 */
+	0x02, 0xbd, 0x08, 0xf7, 0x26, 0x00, 0x00, /* 9 */
+	0x02, 0xbd, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+	0x02, 0xbd, 0x0a, 0xf5, 0x41, 0x00, 0x00, /* mute */
+	0x02, 0xbd, 0x1c, 0xe3, 0x2a, 0x00, 0x00, /* esc */
+	0x02, 0xbd, 0x1f, 0xe0, 0x43, 0x00, 0x00, /* volume up */
+	0x02, 0xbd, 0x12, 0xed, 0x52, 0x00, 0x00, /* up arrow */
+	0x02, 0xbd, 0x11, 0xee, 0x50, 0x00, 0x00, /* left arrow */
+	0x02, 0xbd, 0x15, 0xea, 0x28, 0x00, 0x00, /* ok */
+	0x02, 0xbd, 0x10, 0xef, 0x4f, 0x00, 0x00, /* right arrow */
+	0x02, 0xbd, 0x13, 0xec, 0x51, 0x00, 0x00, /* down arrow */
+	0x02, 0xbd, 0x0e, 0xf1, 0x42, 0x00, 0x00, /* volume down */
+	0x02, 0xbd, 0x19, 0xe6, 0x15, 0x01, 0x00, /* record */
+	0x02, 0xbd, 0x1e, 0xe1, 0x13, 0x03, 0x00, /* play */
+	0x02, 0xbd, 0x16, 0xe9, 0x16, 0x01, 0x00, /* stop */
+	0x02, 0xbd, 0x0b, 0xf4, 0x28, 0x04, 0x00, /* yellow, min / max */
+	0x02, 0xbd, 0x0f, 0xf0, 0x3b, 0x00, 0x00, /* time shift */
+	0x02, 0xbd, 0x18, 0xe7, 0x2e, 0x00, 0x00, /* channel up */
+	0x02, 0xbd, 0x1a, 0xe5, 0x2d, 0x00, 0x00, /* channel down */
+	0x02, 0xbd, 0x17, 0xe8, 0x3e, 0x00, 0x00, /* snapshot */
+	0x02, 0xbd, 0x40, 0xbf, 0x13, 0x01, 0x00, /* pause */
+	0x02, 0xbd, 0x41, 0xbe, 0x09, 0x03, 0x00, /* FF >> */
+	0x02, 0xbd, 0x42, 0xbd, 0x07, 0x03, 0x00, /* FR << */
+	0x02, 0xbd, 0x43, 0xbc, 0x00, 0x00, 0x00, /* 'select' (?) */
+	0x02, 0xbd, 0x44, 0xbb, 0x16, 0x03, 0x00, /* shuffle */
+	0x02, 0xbd, 0x45, 0xba, 0x45, 0x03, 0x00, /* power */
+};
+
+/* KWorld PlusTV Dual DVB-T Stick (DVB-T 399U) */
+static u8 af9015_ir_table_kworld[] = {
+	0x86, 0x6b, 0x0c, 0xf3, 0x2e, 0x07, 0x00,
+	0x86, 0x6b, 0x16, 0xe9, 0x2d, 0x07, 0x00,
+	0x86, 0x6b, 0x1d, 0xe2, 0x37, 0x07, 0x00,
+	0x86, 0x6b, 0x00, 0xff, 0x1e, 0x07, 0x00,
+	0x86, 0x6b, 0x01, 0xfe, 0x1f, 0x07, 0x00,
+	0x86, 0x6b, 0x02, 0xfd, 0x20, 0x07, 0x00,
+	0x86, 0x6b, 0x03, 0xfc, 0x21, 0x07, 0x00,
+	0x86, 0x6b, 0x04, 0xfb, 0x22, 0x07, 0x00,
+	0x86, 0x6b, 0x05, 0xfa, 0x23, 0x07, 0x00,
+	0x86, 0x6b, 0x06, 0xf9, 0x24, 0x07, 0x00,
+	0x86, 0x6b, 0x07, 0xf8, 0x25, 0x07, 0x00,
+	0x86, 0x6b, 0x08, 0xf7, 0x26, 0x07, 0x00,
+	0x86, 0x6b, 0x09, 0xf6, 0x4d, 0x07, 0x00,
+	0x86, 0x6b, 0x0a, 0xf5, 0x4e, 0x07, 0x00,
+	0x86, 0x6b, 0x14, 0xeb, 0x4f, 0x07, 0x00,
+	0x86, 0x6b, 0x1e, 0xe1, 0x50, 0x07, 0x00,
+	0x86, 0x6b, 0x17, 0xe8, 0x52, 0x07, 0x00,
+	0x86, 0x6b, 0x1f, 0xe0, 0x51, 0x07, 0x00,
+	0x86, 0x6b, 0x0e, 0xf1, 0x0b, 0x07, 0x00,
+	0x86, 0x6b, 0x20, 0xdf, 0x0c, 0x07, 0x00,
+	0x86, 0x6b, 0x42, 0xbd, 0x0d, 0x07, 0x00,
+	0x86, 0x6b, 0x0b, 0xf4, 0x0e, 0x07, 0x00,
+	0x86, 0x6b, 0x43, 0xbc, 0x0f, 0x07, 0x00,
+	0x86, 0x6b, 0x10, 0xef, 0x10, 0x07, 0x00,
+	0x86, 0x6b, 0x21, 0xde, 0x11, 0x07, 0x00,
+	0x86, 0x6b, 0x13, 0xec, 0x12, 0x07, 0x00,
+	0x86, 0x6b, 0x11, 0xee, 0x13, 0x07, 0x00,
+	0x86, 0x6b, 0x12, 0xed, 0x14, 0x07, 0x00,
+	0x86, 0x6b, 0x19, 0xe6, 0x15, 0x07, 0x00,
+	0x86, 0x6b, 0x1a, 0xe5, 0x16, 0x07, 0x00,
+	0x86, 0x6b, 0x1b, 0xe4, 0x17, 0x07, 0x00,
+	0x86, 0x6b, 0x4b, 0xb4, 0x18, 0x07, 0x00,
+	0x86, 0x6b, 0x40, 0xbf, 0x19, 0x07, 0x00,
+	0x86, 0x6b, 0x44, 0xbb, 0x1a, 0x07, 0x00,
+	0x86, 0x6b, 0x41, 0xbe, 0x1b, 0x07, 0x00,
+	0x86, 0x6b, 0x22, 0xdd, 0x1c, 0x07, 0x00,
+	0x86, 0x6b, 0x15, 0xea, 0x1d, 0x07, 0x00,
+	0x86, 0x6b, 0x0f, 0xf0, 0x3f, 0x07, 0x00,
+	0x86, 0x6b, 0x1c, 0xe3, 0x40, 0x07, 0x00,
+	0x86, 0x6b, 0x4a, 0xb5, 0x41, 0x07, 0x00,
+	0x86, 0x6b, 0x48, 0xb7, 0x42, 0x07, 0x00,
+	0x86, 0x6b, 0x49, 0xb6, 0x43, 0x07, 0x00,
+	0x86, 0x6b, 0x18, 0xe7, 0x44, 0x07, 0x00,
+	0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00,
+};
+
+#endif
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 81369b7..2558f2b 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -34,12 +34,14 @@
 #define USB_VID_HAUPPAUGE			0x2040
 #define USB_VID_HYPER_PALTEK			0x1025
 #define USB_VID_KWORLD				0xeb2a
+#define USB_VID_KWORLD_2			0x1b80
 #define USB_VID_KYE				0x0458
 #define USB_VID_LEADTEK				0x0413
 #define USB_VID_LITEON				0x04ca
 #define USB_VID_MEDION				0x1660
 #define USB_VID_MIGLIA				0x18f3
 #define USB_VID_MSI				0x0db0
+#define USB_VID_MSI_2				0x1462
 #define USB_VID_OPERA1				0x695c
 #define USB_VID_PINNACLE			0x2304
 #define USB_VID_TECHNOTREND			0x0b48
@@ -51,15 +53,18 @@
 #define USB_VID_WIDEVIEW			0x14aa
 #define USB_VID_GIGABYTE			0x1044
 #define USB_VID_YUAN				0x1164
-
+#define USB_VID_XTENSIONS			0x1ae7
 
 /* Product IDs */
 #define USB_PID_ADSTECH_USB2_COLD			0xa333
 #define USB_PID_ADSTECH_USB2_WARM			0xa334
 #define USB_PID_AFATECH_AF9005				0x9020
+#define USB_PID_AFATECH_AF9015_9015			0x9015
+#define USB_PID_AFATECH_AF9015_9016			0x9016
 #define USB_VID_ALINK_DTU				0xf170
 #define USB_PID_ANSONIC_DVBT_USB			0x6000
 #define USB_PID_ANYSEE					0x861f
+#define USB_PID_AZUREWAVE_AD_TU700			0x3237
 #define USB_PID_AVERMEDIA_DVBT_USB_COLD			0x0001
 #define USB_PID_AVERMEDIA_DVBT_USB_WARM			0x0002
 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD		0xa800
@@ -89,9 +94,12 @@
 #define USB_PID_UNIWILL_STK7700P			0x6003
 #define USB_PID_GRANDTEC_DVBT_USB_COLD			0x0fa0
 #define USB_PID_GRANDTEC_DVBT_USB_WARM			0x0fa1
+#define USB_PID_KWORLD_399U				0xe399
+#define USB_PID_KWORLD_PC160_2T				0xc160
 #define USB_PID_KWORLD_VSTREAM_COLD			0x17de
 #define USB_PID_KWORLD_VSTREAM_WARM			0x17df
 #define USB_PID_TERRATEC_CINERGY_T_USB_XE		0x0055
+#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2		0x0069
 #define USB_PID_TWINHAN_VP7041_COLD			0x3201
 #define USB_PID_TWINHAN_VP7041_WARM			0x3202
 #define USB_PID_TWINHAN_VP7020_COLD			0x3203
@@ -100,6 +108,7 @@
 #define USB_PID_TWINHAN_VP7045_WARM			0x3206
 #define USB_PID_TWINHAN_VP7021_COLD			0x3207
 #define USB_PID_TWINHAN_VP7021_WARM			0x3208
+#define USB_PID_TINYTWIN				0x3226
 #define USB_PID_DNTV_TINYUSB2_COLD			0x3223
 #define USB_PID_DNTV_TINYUSB2_WARM			0x3224
 #define USB_PID_ULTIMA_TVBOX_COLD			0x8105
@@ -146,6 +155,8 @@
 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R	0x0039
 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC	0x1039
 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT	0x2039
+#define USB_PID_AVERMEDIA_VOLAR_X			0xa815
+#define USB_PID_AVERMEDIA_VOLAR_X_2			0x8150
 #define USB_PID_TECHNOTREND_CONNECT_S2400               0x3006
 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY	0x005a
 #define USB_PID_TERRATEC_CINERGY_HT_USB_XE		0x0058
@@ -155,6 +166,7 @@
 #define USB_PID_PINNACLE_PCTV2000E			0x022c
 #define USB_PID_PINNACLE_PCTV_DVB_T_FLASH		0x0228
 #define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T	0x0229
+#define USB_PID_PINNACLE_PCTV71E			0x022b
 #define USB_PID_PINNACLE_PCTV72E			0x0236
 #define USB_PID_PINNACLE_PCTV73E			0x0237
 #define USB_PID_PCTV_200E				0x020e
@@ -193,6 +205,7 @@
 #define USB_PID_WINFAST_DTV_DONGLE_WARM			0x6026
 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P		0x6f00
 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2		0x6f01
+#define USB_PID_WINFAST_DTV_DONGLE_GOLD			0x6029
 #define USB_PID_GENPIX_8PSK_REV_1_COLD			0x0200
 #define USB_PID_GENPIX_8PSK_REV_1_WARM			0x0201
 #define USB_PID_GENPIX_8PSK_REV_2			0x0202
@@ -200,6 +213,7 @@
 #define USB_PID_GENPIX_SKYWALKER_CW3K			0x0204
 #define USB_PID_SIGMATEK_DVB_110			0x6610
 #define USB_PID_MSI_DIGI_VOX_MINI_II			0x1513
+#define USB_PID_MSI_DIGIVOX_DUO				0x8801
 #define USB_PID_OPERA1_COLD				0x2830
 #define USB_PID_OPERA1_WARM				0x3829
 #define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD		0x0514
@@ -209,5 +223,6 @@
 #define USB_PID_ASUS_U3100				0x173f
 #define USB_PID_YUAN_EC372S				0x1edc
 #define USB_PID_DW2102					0x2102
+#define USB_PID_XTENSIONS_XD_380			0x0381
 
 #endif