summaryrefslogtreecommitdiff
path: root/linux
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2008-09-24 10:15:43 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-09-24 10:15:43 -0300
commitddcc13a8b8b15694685a582ab81741fd56a092b6 (patch)
treefcc7adc43f74132bb78fec5e9c0818ef7613bb63 /linux
parent72f8339318334eaa1c489dc63611988e2bc38822 (diff)
parent3c4d506a9fddb6c4a5611b12468a335356e3d25f (diff)
downloadmediapointer-dvb-s2-ddcc13a8b8b15694685a582ab81741fd56a092b6.tar.gz
mediapointer-dvb-s2-ddcc13a8b8b15694685a582ab81741fd56a092b6.tar.bz2
merge: http://linuxtv.org/hg/~anttip/af9015
From: Mauro Carvalho Chehab <mchehab@redhat.com> Priority: normal Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'linux')
-rw-r--r--linux/drivers/media/common/tuners/mt2060.c38
-rw-r--r--linux/drivers/media/dvb/dvb-usb/Kconfig12
-rw-r--r--linux/drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--linux/drivers/media/dvb/dvb-usb/af9015.c1474
-rw-r--r--linux/drivers/media/dvb/dvb-usb/af9015.h524
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h20
-rw-r--r--linux/drivers/media/dvb/frontends/Kconfig6
-rw-r--r--linux/drivers/media/dvb/frontends/Makefile1
-rw-r--r--linux/drivers/media/dvb/frontends/af9013.c1687
-rw-r--r--linux/drivers/media/dvb/frontends/af9013.h107
-rw-r--r--linux/drivers/media/dvb/frontends/af9013_priv.h869
11 files changed, 4738 insertions, 3 deletions
diff --git a/linux/drivers/media/common/tuners/mt2060.c b/linux/drivers/media/common/tuners/mt2060.c
index 355817754..d1962bb24 100644
--- a/linux/drivers/media/common/tuners/mt2060.c
+++ b/linux/drivers/media/common/tuners/mt2060.c
@@ -177,6 +177,9 @@ static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
b[0] = REG_LO1B1;
b[1] = 0xFF;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
mt2060_writeregs(priv,b,2);
freq = params->frequency / 1000; // Hz -> kHz
@@ -240,6 +243,9 @@ static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
i++;
} while (i<10);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
return ret;
}
@@ -303,13 +309,35 @@ static int mt2060_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
static int mt2060_init(struct dvb_frontend *fe)
{
struct mt2060_priv *priv = fe->tuner_priv;
- return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x33);
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ ret = mt2060_writereg(priv, REG_VGAG,
+ (priv->cfg->clock_out << 6) | 0x33);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ return ret;
}
static int mt2060_sleep(struct dvb_frontend *fe)
{
struct mt2060_priv *priv = fe->tuner_priv;
- return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30);
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ ret = mt2060_writereg(priv, REG_VGAG,
+ (priv->cfg->clock_out << 6) | 0x30);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ return ret;
}
static int mt2060_release(struct dvb_frontend *fe)
@@ -351,6 +379,9 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter
priv->i2c = i2c;
priv->if1_freq = if1;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) {
kfree(priv);
return NULL;
@@ -367,6 +398,9 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter
mt2060_calibrate(priv);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
return fe;
}
EXPORT_SYMBOL(mt2060_attach);
diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig
index 1d9e98cee..b35d7f0db 100644
--- a/linux/drivers/media/dvb/dvb-usb/Kconfig
+++ b/linux/drivers/media/dvb/dvb-usb/Kconfig
@@ -271,3 +271,15 @@ config DVB_USB_DTV5100
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/linux/drivers/media/dvb/dvb-usb/Makefile b/linux/drivers/media/dvb/dvb-usb/Makefile
index 116544f42..ece8c9dfe 100644
--- a/linux/drivers/media/dvb/dvb-usb/Makefile
+++ b/linux/drivers/media/dvb/dvb-usb/Makefile
@@ -70,6 +70,9 @@ obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
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/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c
new file mode 100644
index 000000000..776d01d4a
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-usb/af9015.c
@@ -0,0 +1,1474 @@
+/*
+ * 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 /* keep */
+#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 /* keep */
+ 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)},
+ {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)},
+ {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
+ {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 = 5,
+ .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},
+ },
+ {
+ .name = "Telestar Starstick 2",
+ .cold_ids = {&af9015_usb_table[13], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "AVerMedia A309",
+ .cold_ids = {&af9015_usb_table[14], 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 = {
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15)
+ .owner = THIS_MODULE,
+#endif
+ .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/linux/drivers/media/dvb/dvb-usb/af9015.h b/linux/drivers/media/dvb/dvb-usb/af9015.h
new file mode 100644
index 000000000..882e8a4b3
--- /dev/null
+++ b/linux/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/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 81369b79e..7ae262e08 100644
--- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -34,16 +34,19 @@
#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
#define USB_VID_TERRATEC 0x0ccd
+#define USB_VID_TELESTAR 0x10b9
#define USB_VID_VISIONPLUS 0x13d3
#define USB_VID_TWINHAN 0x1822
#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
@@ -51,15 +54,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 +95,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 +109,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 +156,9 @@
#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_AVERMEDIA_A309 0xa309
#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 +168,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 +207,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 +215,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 +225,7 @@
#define USB_PID_ASUS_U3100 0x173f
#define USB_PID_YUAN_EC372S 0x1edc
#define USB_PID_DW2102 0x2102
+#define USB_PID_XTENSIONS_XD_380 0x0381
+#define USB_PID_TELESTAR_STARSTICK_2 0x8000
#endif
diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig
index e2444f270..097d9f001 100644
--- a/linux/drivers/media/dvb/frontends/Kconfig
+++ b/linux/drivers/media/dvb/frontends/Kconfig
@@ -398,4 +398,10 @@ config DVB_DUMMY_FE
tristate "Dummy frontend driver"
default n
+config DVB_AF9013
+ tristate "Afatech AF9013 demodulator"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Say Y when you want to support this frontend.
endmenu
diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile
index ca24618d5..1b5478886 100644
--- a/linux/drivers/media/dvb/frontends/Makefile
+++ b/linux/drivers/media/dvb/frontends/Makefile
@@ -50,3 +50,4 @@ obj-$(CONFIG_DVB_TDA10048) += tda10048.o
obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
+obj-$(CONFIG_DVB_AF9013) += af9013.o
diff --git a/linux/drivers/media/dvb/frontends/af9013.c b/linux/drivers/media/dvb/frontends/af9013.c
new file mode 100644
index 000000000..739d885b3
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/af9013.c
@@ -0,0 +1,1687 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "af9013_priv.h"
+#include "af9013.h"
+
+int debug;
+
+struct af9013_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend frontend;
+
+ struct af9013_config config;
+
+ u16 signal_strength;
+ u32 ber;
+ u32 ucblocks;
+ u16 snr;
+ u32 frequency;
+ unsigned long next_statistics_check;
+};
+
+static u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
+
+static int af9013_write_regs(struct af9013_state *state, u8 mbox, u16 reg,
+ u8 *val, u8 len)
+{
+ u8 buf[3+len];
+ struct i2c_msg msg = {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ buf[2] = mbox;
+ memcpy(&buf[3], val, len);
+
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ warn("I2C write failed reg:%04x len:%d", reg, len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int af9013_write_ofdm_regs(struct af9013_state *state, u16 reg, u8 *val,
+ u8 len)
+{
+ u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(0 << 6)|(0 << 7);
+ return af9013_write_regs(state, mbox, reg, val, len);
+}
+
+static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val,
+ u8 len)
+{
+ u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(1 << 6)|(1 << 7);
+ return af9013_write_regs(state, mbox, reg, val, len);
+}
+
+/* write single register */
+static int af9013_write_reg(struct af9013_state *state, u16 reg, u8 val)
+{
+ return af9013_write_ofdm_regs(state, reg, &val, 1);
+}
+
+/* read single register */
+static int af9013_read_reg(struct af9013_state *state, u16 reg, u8 *val)
+{
+ u8 obuf[3] = { reg >> 8, reg & 0xff, 0 };
+ u8 ibuf[1];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = sizeof(obuf),
+ .buf = obuf
+ }, {
+ .addr = state->config.demod_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(ibuf),
+ .buf = ibuf
+ }
+ };
+
+ if (i2c_transfer(state->i2c, msg, 2) != 2) {
+ warn("I2C read failed reg:%04x", reg);
+ return -EREMOTEIO;
+ }
+ *val = ibuf[0];
+ return 0;
+}
+
+static int af9013_write_reg_bits(struct af9013_state *state, u16 reg, u8 pos,
+ u8 len, u8 val)
+{
+ int ret;
+ u8 tmp, mask;
+
+ ret = af9013_read_reg(state, reg, &tmp);
+ if (ret)
+ return ret;
+
+ mask = regmask[len - 1] << pos;
+ tmp = (tmp & ~mask) | ((val << pos) & mask);
+
+ return af9013_write_reg(state, reg, tmp);
+}
+
+static int af9013_read_reg_bits(struct af9013_state *state, u16 reg, u8 pos,
+ u8 len, u8 *val)
+{
+ int ret;
+ u8 tmp;
+
+ ret = af9013_read_reg(state, reg, &tmp);
+ if (ret)
+ return ret;
+ *val = (tmp >> pos) & regmask[len - 1];
+ return 0;
+}
+
+static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
+{
+ int ret;
+ u8 pos;
+ u16 addr;
+ deb_info("%s: gpio:%d gpioval:%02x\n", __func__, gpio, gpioval);
+
+/* GPIO0 & GPIO1 0xd735
+ GPIO2 & GPIO3 0xd736 */
+
+ switch (gpio) {
+ case 0:
+ case 1:
+ addr = 0xd735;
+ break;
+ case 2:
+ case 3:
+ addr = 0xd736;
+ break;
+
+ default:
+ err("invalid gpio:%d\n", gpio);
+ ret = -EINVAL;
+ goto error;
+ };
+
+ switch (gpio) {
+ case 0:
+ case 2:
+ pos = 0;
+ break;
+ case 1:
+ case 3:
+ default:
+ pos = 4;
+ break;
+ };
+
+ ret = af9013_write_reg_bits(state, addr, pos, 4, gpioval);
+
+error:
+ return ret;
+}
+
+static u32 af913_div(u32 a, u32 b, u32 x)
+{
+ u32 r = 0, c = 0, i;
+ deb_info("%s: a:%d b:%d x:%d\n", __func__, a, b, x);
+
+ if (a > b) {
+ c = a / b;
+ a = a - c * b;
+ }
+
+ for (i = 0; i < x; i++) {
+ if (a >= b) {
+ r += 1;
+ a -= b;
+ }
+ a <<= 1;
+ r <<= 1;
+ }
+ r = (c << (u32)x) + r;
+
+ deb_info("%s: a:%d b:%d x:%d r:%d r:%x\n", __func__, a, b, x, r, r);
+ return r;
+}
+
+static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw)
+{
+ int ret = 0;
+ u8 i = 0;
+ u8 buf[24];
+ u32 ns_coeff1_2048nu;
+ u32 ns_coeff1_8191nu;
+ u32 ns_coeff1_8192nu;
+ u32 ns_coeff1_8193nu;
+ u32 ns_coeff2_2k;
+ u32 ns_coeff2_8k;
+
+ deb_info("%s: adc_clock:%d bw:%d\n", __func__,
+ state->config.adc_clock, bw);
+
+ switch (state->config.adc_clock) {
+ case 28800: /* 28.800 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x01e79e7a;
+ ns_coeff1_8191nu = 0x0079eb6e;
+ ns_coeff1_8192nu = 0x0079e79e;
+ ns_coeff1_8193nu = 0x0079e3cf;
+ ns_coeff2_2k = 0x00f3cf3d;
+ ns_coeff2_8k = 0x003cf3cf;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x0238e38e;
+ ns_coeff1_8191nu = 0x008e3d55;
+ ns_coeff1_8192nu = 0x008e38e4;
+ ns_coeff1_8193nu = 0x008e3472;
+ ns_coeff2_2k = 0x011c71c7;
+ ns_coeff2_8k = 0x00471c72;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x028a28a3;
+ ns_coeff1_8191nu = 0x00a28f3d;
+ ns_coeff1_8192nu = 0x00a28a29;
+ ns_coeff1_8193nu = 0x00a28514;
+ ns_coeff2_2k = 0x01451451;
+ ns_coeff2_8k = 0x00514514;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case 20480: /* 20.480 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x02adb6dc;
+ ns_coeff1_8191nu = 0x00ab7313;
+ ns_coeff1_8192nu = 0x00ab6db7;
+ ns_coeff1_8193nu = 0x00ab685c;
+ ns_coeff2_2k = 0x0156db6e;
+ ns_coeff2_8k = 0x0055b6dc;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x03200001;
+ ns_coeff1_8191nu = 0x00c80640;
+ ns_coeff1_8192nu = 0x00c80000;
+ ns_coeff1_8193nu = 0x00c7f9c0;
+ ns_coeff2_2k = 0x01900000;
+ ns_coeff2_8k = 0x00640000;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x03924926;
+ ns_coeff1_8191nu = 0x00e4996e;
+ ns_coeff1_8192nu = 0x00e49249;
+ ns_coeff1_8193nu = 0x00e48b25;
+ ns_coeff2_2k = 0x01c92493;
+ ns_coeff2_8k = 0x00724925;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case 28000: /* 28.000 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x01f58d10;
+ ns_coeff1_8191nu = 0x007d672f;
+ ns_coeff1_8192nu = 0x007d6344;
+ ns_coeff1_8193nu = 0x007d5f59;
+ ns_coeff2_2k = 0x00fac688;
+ ns_coeff2_8k = 0x003eb1a2;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x02492492;
+ ns_coeff1_8191nu = 0x00924db7;
+ ns_coeff1_8192nu = 0x00924925;
+ ns_coeff1_8193nu = 0x00924492;
+ ns_coeff2_2k = 0x01249249;
+ ns_coeff2_8k = 0x00492492;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x029cbc15;
+ ns_coeff1_8191nu = 0x00a7343f;
+ ns_coeff1_8192nu = 0x00a72f05;
+ ns_coeff1_8193nu = 0x00a729cc;
+ ns_coeff2_2k = 0x014e5e0a;
+ ns_coeff2_8k = 0x00539783;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case 25000: /* 25.000 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x0231bcb5;
+ ns_coeff1_8191nu = 0x008c7391;
+ ns_coeff1_8192nu = 0x008c6f2d;
+ ns_coeff1_8193nu = 0x008c6aca;
+ ns_coeff2_2k = 0x0118de5b;
+ ns_coeff2_8k = 0x00463797;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x028f5c29;
+ ns_coeff1_8191nu = 0x00a3dc29;
+ ns_coeff1_8192nu = 0x00a3d70a;
+ ns_coeff1_8193nu = 0x00a3d1ec;
+ ns_coeff2_2k = 0x0147ae14;
+ ns_coeff2_8k = 0x0051eb85;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x02ecfb9d;
+ ns_coeff1_8191nu = 0x00bb44c1;
+ ns_coeff1_8192nu = 0x00bb3ee7;
+ ns_coeff1_8193nu = 0x00bb390d;
+ ns_coeff2_2k = 0x01767dce;
+ ns_coeff2_8k = 0x005d9f74;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ err("invalid xtal");
+ return -EINVAL;
+ }
+ if (ret) {
+ err("invalid bandwidth");
+ return ret;
+ }
+
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x00ff0000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x01c00000) >> 22);
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x003fc000) >> 14);
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x00003fc0) >> 6);
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x0000003f));
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x00ffc000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x00ffc000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x00ffc000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x01c00000) >> 22);
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x003fc000) >> 14);
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x00003fc0) >> 6);
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x0000003f));
+
+ deb_info("%s: coeff:", __func__);
+ debug_dump(buf, sizeof(buf), deb_info);
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, 0xae00 + i, buf[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int af9013_set_adc_ctrl(struct af9013_state *state)
+{
+ int ret;
+ u8 buf[3], tmp, i;
+ u32 adc_cw;
+
+ deb_info("%s: adc_clock:%d\n", __func__, state->config.adc_clock);
+
+ /* adc frequency type */
+ switch (state->config.adc_clock) {
+ case 28800: /* 28.800 MHz */
+ tmp = 0;
+ break;
+ case 20480: /* 20.480 MHz */
+ tmp = 1;
+ break;
+ case 28000: /* 28.000 MHz */
+ tmp = 2;
+ break;
+ case 25000: /* 25.000 MHz */
+ tmp = 3;
+ break;
+ default:
+ err("invalid xtal");
+ return -EINVAL;
+ }
+
+ adc_cw = af913_div(state->config.adc_clock*1000, 1000000ul, 19ul);
+
+ buf[0] = (u8) ((adc_cw & 0x000000ff));
+ buf[1] = (u8) ((adc_cw & 0x0000ff00) >> 8);
+ buf[2] = (u8) ((adc_cw & 0x00ff0000) >> 16);
+
+ deb_info("%s: adc_cw:", __func__);
+ debug_dump(buf, sizeof(buf), deb_info);
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, 0xd180 + i, buf[i]);
+ if (ret)
+ goto error;
+ }
+ ret = af9013_write_reg_bits(state, 0x9bd2, 0, 4, tmp);
+error:
+ return ret;
+}
+
+static int af9013_set_freq_ctrl(struct af9013_state *state, fe_bandwidth_t bw)
+{
+ int ret;
+ u16 addr;
+ u8 buf[3], i, j;
+ u32 adc_freq, freq_cw;
+ s8 bfs_spec_inv;
+ int if_sample_freq;
+
+ for (j = 0; j < 3; j++) {
+ if (j == 0) {
+ addr = 0xd140; /* fcw normal */
+ bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1;
+ } else if (j == 1) {
+ addr = 0x9be7; /* fcw dummy ram */
+ bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1;
+ } else {
+ addr = 0x9bea; /* fcw inverted */
+ bfs_spec_inv = state->config.rf_spec_inv ? 1 : -1;
+ }
+
+ adc_freq = state->config.adc_clock * 1000;
+ if_sample_freq = state->config.tuner_if * 1000;
+
+ /* TDA18271 uses different sampling freq for every bw */
+ if (state->config.tuner == AF9013_TUNER_TDA18271) {
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ if_sample_freq = 3300000; /* 3.3 MHz */
+ break;
+ case BANDWIDTH_7_MHZ:
+ if_sample_freq = 3800000; /* 3.8 MHz */
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ if_sample_freq = 4300000; /* 4.3 MHz */
+ break;
+ }
+ }
+
+ while (if_sample_freq > (adc_freq / 2))
+ if_sample_freq = if_sample_freq - adc_freq;
+
+ if (if_sample_freq >= 0)
+ bfs_spec_inv = bfs_spec_inv * (-1);
+ else
+ if_sample_freq = if_sample_freq * (-1);
+
+ freq_cw = af913_div(if_sample_freq, adc_freq, 23ul);
+
+ if (bfs_spec_inv == -1)
+ freq_cw = 0x00800000 - freq_cw;
+
+ buf[0] = (u8) ((freq_cw & 0x000000ff));
+ buf[1] = (u8) ((freq_cw & 0x0000ff00) >> 8);
+ buf[2] = (u8) ((freq_cw & 0x007f0000) >> 16);
+
+
+ deb_info("%s: freq_cw:", __func__);
+ debug_dump(buf, sizeof(buf), deb_info);
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, addr++, buf[i]);
+ if (ret)
+ goto error;
+ }
+ }
+error:
+ return ret;
+}
+
+static int af9013_set_ofdm_params(struct af9013_state *state,
+ struct dvb_ofdm_parameters *params, u8 *auto_mode)
+{
+ int ret;
+ u8 i, buf[3] = {0, 0, 0};
+ *auto_mode = 0; /* set if parameters are requested to auto set */
+
+ switch (params->transmission_mode) {
+ case TRANSMISSION_MODE_AUTO:
+ *auto_mode = 1;
+ case TRANSMISSION_MODE_2K:
+ break;
+ case TRANSMISSION_MODE_8K:
+ buf[0] |= (1 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->guard_interval) {
+ case GUARD_INTERVAL_AUTO:
+ *auto_mode = 1;
+ case GUARD_INTERVAL_1_32:
+ break;
+ case GUARD_INTERVAL_1_16:
+ buf[0] |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ buf[0] |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ buf[0] |= (3 << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->hierarchy_information) {
+ case HIERARCHY_AUTO:
+ *auto_mode = 1;
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ buf[0] |= (1 << 4);
+ break;
+ case HIERARCHY_2:
+ buf[0] |= (2 << 4);
+ break;
+ case HIERARCHY_4:
+ buf[0] |= (3 << 4);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ switch (params->constellation) {
+ case QAM_AUTO:
+ *auto_mode = 1;
+ case QPSK:
+ break;
+ case QAM_16:
+ buf[1] |= (1 << 6);
+ break;
+ case QAM_64:
+ buf[1] |= (2 << 6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Use HP. How and which case we can switch to LP? */
+ buf[1] |= (1 << 4);
+
+ switch (params->code_rate_HP) {
+ case FEC_AUTO:
+ *auto_mode = 1;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 0);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 0);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 0);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->code_rate_LP) {
+ case FEC_AUTO:
+ /* if HIERARCHY_NONE and FEC_NONE then LP FEC is set to FEC_AUTO
+ by dvb_frontend.c for compatibility */
+ if (params->hierarchy_information != HIERARCHY_NONE)
+ *auto_mode = 1;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 3);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 3);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 3);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 3);
+ break;
+ case FEC_NONE:
+ if (params->hierarchy_information == HIERARCHY_AUTO)
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ break;
+ case BANDWIDTH_7_MHZ:
+ buf[1] |= (1 << 2);
+ break;
+ case BANDWIDTH_8_MHZ:
+ buf[1] |= (2 << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, 0xd3c0 + i, buf[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int af9013_reset(struct af9013_state *state, u8 sleep)
+{
+ int ret;
+ u8 tmp, i;
+ deb_info("%s\n", __func__);
+
+ /* enable OFDM reset */
+ ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 1);
+ if (ret)
+ goto error;
+
+ /* start reset mechanism */
+ ret = af9013_write_reg(state, 0xaeff, 1);
+ if (ret)
+ goto error;
+
+ /* reset is done when bit 1 is set */
+ for (i = 0; i < 150; i++) {
+ ret = af9013_read_reg_bits(state, 0xd417, 1, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ break; /* reset done */
+ msleep(10);
+ }
+ if (!tmp)
+ return -ETIMEDOUT;
+
+ /* don't clear reset when going to sleep */
+ if (!sleep) {
+ /* clear OFDM reset */
+ ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0);
+ if (ret)
+ goto error;
+
+ /* disable OFDM reset */
+ ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0);
+ }
+error:
+ return ret;
+}
+
+static int af9013_power_ctrl(struct af9013_state *state, u8 onoff)
+{
+ int ret;
+ deb_info("%s: onoff:%d\n", __func__, onoff);
+
+ if (onoff) {
+ /* power on */
+ ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 0);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0);
+ } else {
+ /* power off */
+ ret = af9013_reset(state, 1);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 1);
+ }
+error:
+ return ret;
+}
+
+static int af9013_lock_led(struct af9013_state *state, u8 onoff)
+{
+ deb_info("%s: onoff:%d\n", __func__, onoff);
+
+ return af9013_write_reg_bits(state, 0xd730, 0, 1, onoff);
+}
+
+static int af9013_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 auto_mode; /* auto set TPS */
+
+ deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency,
+ params->u.ofdm.bandwidth);
+
+ state->frequency = params->frequency;
+
+ /* program CFOE coefficients */
+ ret = af9013_set_coeff(state, params->u.ofdm.bandwidth);
+ if (ret)
+ goto error;
+
+ /* program frequency control */
+ ret = af9013_set_freq_ctrl(state, params->u.ofdm.bandwidth);
+ if (ret)
+ goto error;
+
+ /* clear TPS lock flag (inverted flag) */
+ ret = af9013_write_reg_bits(state, 0xd330, 3, 1, 1);
+ if (ret)
+ goto error;
+
+ /* clear MPEG2 lock flag */
+ ret = af9013_write_reg_bits(state, 0xd507, 6, 1, 0);
+ if (ret)
+ goto error;
+
+ /* empty channel function */
+ ret = af9013_write_reg_bits(state, 0x9bfe, 0, 1, 0);
+ if (ret)
+ goto error;
+
+ /* empty DVB-T channel function */
+ ret = af9013_write_reg_bits(state, 0x9bc2, 0, 1, 0);
+ if (ret)
+ goto error;
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, params);
+
+ /* program TPS and bandwidth, check if auto mode needed */
+ ret = af9013_set_ofdm_params(state, &params->u.ofdm, &auto_mode);
+ if (ret)
+ goto error;
+
+ if (auto_mode) {
+ /* clear easy mode flag */
+ ret = af9013_write_reg(state, 0xaefd, 0);
+ deb_info("%s: auto TPS\n", __func__);
+ } else {
+ /* set easy mode flag */
+ ret = af9013_write_reg(state, 0xaefd, 1);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg(state, 0xaefe, 0);
+ deb_info("%s: manual TPS\n", __func__);
+ }
+ if (ret)
+ goto error;
+
+ /* everything is set, lets try to receive channel - OFSM GO! */
+ ret = af9013_write_reg(state, 0xffff, 0);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static int af9013_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 i, buf[3];
+ deb_info("%s\n", __func__);
+
+ /* read TPS registers */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0xd3c0 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+
+ switch ((buf[1] >> 6) & 3) {
+ case 0:
+ p->u.ofdm.constellation = QPSK;
+ break;
+ case 1:
+ p->u.ofdm.constellation = QAM_16;
+ break;
+ case 2:
+ p->u.ofdm.constellation = QAM_64;
+ break;
+ }
+
+ switch ((buf[0] >> 0) & 3) {
+ case 0:
+ p->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ p->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ p->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ p->u.ofdm.hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ p->u.ofdm.hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ p->u.ofdm.hierarchy_information = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[2] >> 0) & 7) {
+ case 0:
+ p->u.ofdm.code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ p->u.ofdm.code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ p->u.ofdm.code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ p->u.ofdm.code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ p->u.ofdm.code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[2] >> 3) & 7) {
+ case 0:
+ p->u.ofdm.code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ p->u.ofdm.code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ p->u.ofdm.code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ p->u.ofdm.code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ p->u.ofdm.code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 2) & 3) {
+ case 0:
+ p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ break;
+ case 1:
+ p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ break;
+ case 2:
+ p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ break;
+ }
+
+ p->inversion = INVERSION_AUTO;
+ p->frequency = state->frequency;
+
+error:
+ return ret;
+}
+
+static int af9013_update_ber_unc(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[3], i;
+ u32 error_bit_count = 0;
+ u32 total_bit_count = 0;
+ u32 abort_packet_count = 0;
+
+ state->ber = 0;
+
+ /* check if error bit count is ready */
+ ret = af9013_read_reg_bits(state, 0xd391, 4, 1, &buf[0]);
+ if (ret)
+ goto error;
+ if (!buf[0])
+ goto exit;
+
+ /* get RSD packet abort count */
+ for (i = 0; i < 2; i++) {
+ ret = af9013_read_reg(state, 0xd38a + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ abort_packet_count = (buf[1] << 8) + buf[0];
+
+ /* get error bit count */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0xd387 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ error_bit_count = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+ error_bit_count = error_bit_count - abort_packet_count * 8 * 8;
+
+ /* get used RSD counting period (10000 RSD packets used) */
+ for (i = 0; i < 2; i++) {
+ ret = af9013_read_reg(state, 0xd385 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ total_bit_count = (buf[1] << 8) + buf[0];
+ total_bit_count = total_bit_count - abort_packet_count;
+ total_bit_count = total_bit_count * 204 * 8;
+
+ if (total_bit_count)
+ state->ber = error_bit_count * 1000000000 / total_bit_count;
+
+ state->ucblocks += abort_packet_count;
+
+ deb_info("%s: err bits:%d total bits:%d abort count:%d\n", __func__,
+ error_bit_count, total_bit_count, abort_packet_count);
+
+ /* set BER counting range */
+ ret = af9013_write_reg(state, 0xd385, 10000 & 0xff);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg(state, 0xd386, 10000 >> 8);
+ if (ret)
+ goto error;
+ /* reset and start BER counter */
+ ret = af9013_write_reg_bits(state, 0xd391, 4, 1, 1);
+ if (ret)
+ goto error;
+
+exit:
+error:
+ return ret;
+}
+
+static int af9013_update_snr(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[3], i, len;
+ u32 quant = 0;
+ struct snr_table *snr_table;
+
+ /* check if quantizer ready (for snr) */
+ ret = af9013_read_reg_bits(state, 0xd2e1, 3, 1, &buf[0]);
+ if (ret)
+ goto error;
+ if (buf[0]) {
+ /* quantizer ready - read it */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0xd2e3 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ quant = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+
+ /* read current constellation */
+ ret = af9013_read_reg(state, 0xd3c1, &buf[0]);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 6) & 3) {
+ case 0:
+ len = ARRAY_SIZE(qpsk_snr_table);
+ snr_table = qpsk_snr_table;
+ break;
+ case 1:
+ len = ARRAY_SIZE(qam16_snr_table);
+ snr_table = qam16_snr_table;
+ break;
+ case 2:
+ len = ARRAY_SIZE(qam64_snr_table);
+ snr_table = qam64_snr_table;
+ break;
+ default:
+ len = 0;
+ break;
+ }
+
+ if (len) {
+ for (i = 0; i < len; i++) {
+ if (quant < snr_table[i].val) {
+ state->snr = snr_table[i].snr * 10;
+ break;
+ }
+ }
+ }
+
+ /* set quantizer super frame count */
+ ret = af9013_write_reg(state, 0xd2e2, 1);
+ if (ret)
+ goto error;
+
+ /* check quantizer availability */
+ for (i = 0; i < 10; i++) {
+ msleep(10);
+ ret = af9013_read_reg_bits(state, 0xd2e6, 0, 1,
+ &buf[0]);
+ if (ret)
+ goto error;
+ if (!buf[0])
+ break;
+ }
+
+ /* reset quantizer */
+ ret = af9013_write_reg_bits(state, 0xd2e1, 3, 1, 1);
+ if (ret)
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+static int af9013_update_signal_strength(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp0;
+ u8 rf_gain, rf_50, rf_80, if_gain, if_50, if_80;
+ int signal_strength;
+
+ deb_info("%s\n", __func__);
+
+ state->signal_strength = 0;
+
+ ret = af9013_read_reg_bits(state, 0x9bee, 0, 1, &tmp0);
+ if (ret)
+ goto error;
+ if (tmp0) {
+ ret = af9013_read_reg(state, 0x9bbd, &rf_50);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0x9bd0, &rf_80);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0x9be2, &if_50);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0x9be4, &if_80);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0xd07c, &rf_gain);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0xd07d, &if_gain);
+ if (ret)
+ goto error;
+ signal_strength = (0xffff / (9 * (rf_50 + if_50) - \
+ 11 * (rf_80 + if_80))) * (10 * (rf_gain + if_gain) - \
+ 11 * (rf_80 + if_80));
+ if (signal_strength < 0)
+ signal_strength = 0;
+ else if (signal_strength > 0xffff)
+ signal_strength = 0xffff;
+
+ state->signal_strength = signal_strength;
+ }
+
+error:
+ return ret;
+}
+
+static int af9013_update_statistics(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+
+ if (time_before(jiffies, state->next_statistics_check))
+ return 0;
+
+ /* set minimum statistic update interval */
+ state->next_statistics_check = jiffies + msecs_to_jiffies(1200);
+
+ ret = af9013_update_signal_strength(fe);
+ if (ret)
+ goto error;
+ ret = af9013_update_snr(fe);
+ if (ret)
+ goto error;
+ ret = af9013_update_ber_unc(fe);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static int af9013_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 800;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret = 0;
+ u8 tmp;
+ *status = 0;
+
+ /* TPS lock */
+ ret = af9013_read_reg_bits(state, 0xd330, 3, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL;
+
+ /* MPEG2 lock */
+ ret = af9013_read_reg_bits(state, 0xd507, 6, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+ if (!*status & FE_HAS_SIGNAL) {
+ /* AGC lock */
+ ret = af9013_read_reg_bits(state, 0xd1a0, 6, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_SIGNAL;
+ }
+
+ if (!*status & FE_HAS_CARRIER) {
+ /* CFO lock */
+ ret = af9013_read_reg_bits(state, 0xd333, 7, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_CARRIER;
+ }
+
+ if (!*status & FE_HAS_CARRIER) {
+ /* SFOE lock */
+ ret = af9013_read_reg_bits(state, 0xd334, 6, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_CARRIER;
+ }
+
+ ret = af9013_update_statistics(fe);
+
+error:
+ return ret;
+}
+
+
+static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *ber = state->ber;
+ return ret;
+}
+
+static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *strength = state->signal_strength;
+ return ret;
+}
+
+static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *snr = state->snr;
+ return ret;
+}
+
+static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *ucblocks = state->ucblocks;
+ return ret;
+}
+
+static int af9013_sleep(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ deb_info("%s\n", __func__);
+
+ ret = af9013_lock_led(state, 0);
+ if (ret)
+ goto error;
+
+ ret = af9013_power_ctrl(state, 0);
+error:
+ return ret;
+}
+
+static int af9013_init(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret, i, len;
+ u8 tmp0, tmp1;
+ struct regdesc *init;
+ deb_info("%s\n", __func__);
+
+ /* reset OFDM */
+ ret = af9013_reset(state, 0);
+ if (ret)
+ goto error;
+
+ /* power on */
+ ret = af9013_power_ctrl(state, 1);
+ if (ret)
+ goto error;
+
+ /* enable ADC */
+ ret = af9013_write_reg(state, 0xd73a, 0xa4);
+ if (ret)
+ goto error;
+
+ /* write API version to firmware */
+ for (i = 0; i < sizeof(state->config.api_version); i++) {
+ ret = af9013_write_reg(state, 0x9bf2 + i,
+ state->config.api_version[i]);
+ if (ret)
+ goto error;
+ }
+
+ /* program ADC control */
+ ret = af9013_set_adc_ctrl(state);
+ if (ret)
+ goto error;
+
+ /* set I2C master clock */
+ ret = af9013_write_reg(state, 0xd416, 0x14);
+ if (ret)
+ goto error;
+
+ /* set 16 embx */
+ ret = af9013_write_reg_bits(state, 0xd700, 1, 1, 1);
+ if (ret)
+ goto error;
+
+ /* set no trigger */
+ ret = af9013_write_reg_bits(state, 0xd700, 2, 1, 0);
+ if (ret)
+ goto error;
+
+ /* set read-update bit for constellation */
+ ret = af9013_write_reg_bits(state, 0xd371, 1, 1, 1);
+ if (ret)
+ goto error;
+
+ /* enable FEC monitor */
+ ret = af9013_write_reg_bits(state, 0xd392, 1, 1, 1);
+ if (ret)
+ goto error;
+
+ /* load OFSM settings */
+ deb_info("%s: load ofsm settings\n", __func__);
+ len = ARRAY_SIZE(ofsm_init);
+ init = ofsm_init;
+ for (i = 0; i < len; i++) {
+ ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos,
+ init[i].len, init[i].val);
+ if (ret)
+ goto error;
+ }
+
+ /* load tuner specific settings */
+ deb_info("%s: load tuner specific settings\n", __func__);
+ switch (state->config.tuner) {
+ case AF9013_TUNER_MXL5003D:
+ len = ARRAY_SIZE(tuner_init_mxl5003d);
+ init = tuner_init_mxl5003d;
+ break;
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ len = ARRAY_SIZE(tuner_init_mxl5005);
+ init = tuner_init_mxl5005;
+ break;
+ case AF9013_TUNER_ENV77H11D5:
+ len = ARRAY_SIZE(tuner_init_env77h11d5);
+ init = tuner_init_env77h11d5;
+ break;
+ case AF9013_TUNER_MT2060:
+ len = ARRAY_SIZE(tuner_init_mt2060);
+ init = tuner_init_mt2060;
+ break;
+ case AF9013_TUNER_MC44S803:
+ len = ARRAY_SIZE(tuner_init_mc44s803);
+ init = tuner_init_mc44s803;
+ break;
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_QT1010A:
+ len = ARRAY_SIZE(tuner_init_qt1010);
+ init = tuner_init_qt1010;
+ break;
+ case AF9013_TUNER_MT2060_2:
+ len = ARRAY_SIZE(tuner_init_mt2060_2);
+ init = tuner_init_mt2060_2;
+ break;
+ case AF9013_TUNER_TDA18271:
+ len = ARRAY_SIZE(tuner_init_tda18271);
+ init = tuner_init_tda18271;
+ break;
+ case AF9013_TUNER_UNKNOWN:
+ default:
+ len = ARRAY_SIZE(tuner_init_unknown);
+ init = tuner_init_unknown;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos,
+ init[i].len, init[i].val);
+ if (ret)
+ goto error;
+ }
+
+ /* set TS mode */
+ deb_info("%s: setting ts mode\n", __func__);
+ tmp0 = 0; /* parallel mode */
+ tmp1 = 0; /* serial mode */
+ switch (state->config.output_mode) {
+ case AF9013_OUTPUT_MODE_PARALLEL:
+ tmp0 = 1;
+ break;
+ case AF9013_OUTPUT_MODE_SERIAL:
+ tmp1 = 1;
+ break;
+ case AF9013_OUTPUT_MODE_USB:
+ /* usb mode for AF9015 */
+ default:
+ break;
+ }
+ ret = af9013_write_reg_bits(state, 0xd500, 1, 1, tmp0); /* parallel */
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd500, 2, 1, tmp1); /* serial */
+ if (ret)
+ goto error;
+
+ /* enable lock led */
+ ret = af9013_lock_led(state, 1);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static struct dvb_frontend_ops af9013_ops;
+
+static int af9013_download_firmware(struct af9013_state *state)
+{
+ int i, len, packets, remainder, ret;
+ const struct firmware *fw;
+ u16 addr = 0x5100; /* firmware start address */
+ u16 checksum = 0;
+ u8 val;
+ u8 fw_params[4];
+ u8 *data;
+ u8 *fw_file = AF9013_DEFAULT_FIRMWARE;
+
+ msleep(100);
+ /* check whether firmware is already running */
+ ret = af9013_read_reg(state, 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;
+
+ info("found a '%s' in cold state, will try to load a firmware",
+ af9013_ops.info.name);
+
+ /* request the firmware, this will block and timeout */
+ ret = request_firmware(&fw, fw_file, &state->i2c->dev);
+ if (ret) {
+ err("did not find the firmware file. (%s) "
+ "Please see linux/Documentation/dvb/ for more details" \
+ " on firmware-problems. (%d)",
+ fw_file, ret);
+ goto error;
+ }
+
+ info("downloading firmware from file '%s'", fw_file);
+
+ /* calc checksum */
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+
+ fw_params[0] = checksum >> 8;
+ fw_params[1] = checksum & 0xff;
+ fw_params[2] = fw->size >> 8;
+ fw_params[3] = fw->size & 0xff;
+
+ /* write fw checksum & size */
+ ret = af9013_write_ofsm_regs(state, 0x50fc,
+ fw_params, sizeof(fw_params));
+ if (ret)
+ goto error_release;
+
+ #define FW_PACKET_MAX_DATA 16
+
+ 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;
+
+ data = (fw->data + i * FW_PACKET_MAX_DATA);
+ ret = af9013_write_ofsm_regs(state, addr, data, len);
+ addr += FW_PACKET_MAX_DATA;
+
+ if (ret) {
+ err("firmware download failed at %d with %d", i, ret);
+ goto error_release;
+ }
+ }
+
+ #undef FW_PACKET_MAX_DATA
+
+ /* request boot firmware */
+ ret = af9013_write_reg(state, 0xe205, 1);
+ if (ret)
+ goto error_release;
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ /* check firmware status */
+ ret = af9013_read_reg(state, 0x98be, &val);
+ if (ret)
+ goto error_release;
+
+ deb_info("%s: firmware status:%02x\n", __func__, val);
+
+ 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_release:
+ release_firmware(fw);
+error:
+exit:
+ if (!ret)
+ info("found a '%s' in warm state.", af9013_ops.info.name);
+ return ret;
+}
+
+static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int ret;
+ struct af9013_state *state = fe->demodulator_priv;
+ deb_info("%s: enable:%d\n", __func__, enable);
+
+ if (state->config.output_mode == AF9013_OUTPUT_MODE_USB)
+ ret = af9013_write_reg_bits(state, 0xd417, 3, 1, enable);
+ else
+ ret = af9013_write_reg_bits(state, 0xd607, 2, 1, enable);
+
+ return ret;
+}
+
+static void af9013_release(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops af9013_ops;
+
+struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct af9013_state *state = NULL;
+ u8 buf[3], i;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->config, config, sizeof(struct af9013_config));
+
+ /* chip version */
+ ret = af9013_read_reg_bits(state, 0xd733, 4, 4, &buf[2]);
+ if (ret)
+ goto error;
+
+ /* ROM version */
+ for (i = 0; i < 2; i++) {
+ ret = af9013_read_reg(state, 0x116b + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ deb_info("%s: chip version:%d ROM version:%d.%d\n", __func__,
+ buf[2], buf[0], buf[1]);
+
+ /* download firmware */
+ if (state->config.output_mode != AF9013_OUTPUT_MODE_USB) {
+ ret = af9013_download_firmware(state);
+ if (ret)
+ goto error;
+ }
+
+ /* firmware version */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0x5103 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ info("firmware version:%d.%d.%d", buf[0], buf[1], buf[2]);
+
+ /* settings for mp2if */
+ if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) {
+ /* AF9015 split PSB to 1.5k + 0.5k */
+ ret = af9013_write_reg_bits(state, 0xd50b, 2, 1, 1);
+ } else {
+ /* AF9013 change the output bit to data7 */
+ ret = af9013_write_reg_bits(state, 0xd500, 3, 1, 1);
+ if (ret)
+ goto error;
+ /* AF9013 set mpeg to full speed */
+ ret = af9013_write_reg_bits(state, 0xd502, 4, 1, 1);
+ }
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd520, 4, 1, 1);
+ if (ret)
+ goto error;
+
+ /* set GPIOs */
+ for (i = 0; i < sizeof(state->config.gpio); i++) {
+ ret = af9013_set_gpio(state, i, state->config.gpio[i]);
+ if (ret)
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &af9013_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ return &state->frontend;
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(af9013_attach);
+
+static struct dvb_frontend_ops af9013_ops = {
+ .info = {
+ .name = "Afatech AF9013 DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 250000,
+ .frequency_tolerance = 0,
+ .caps =
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = af9013_release,
+ .init = af9013_init,
+ .sleep = af9013_sleep,
+ .i2c_gate_ctrl = af9013_i2c_gate_ctrl,
+
+ .set_frontend = af9013_set_frontend,
+ .get_frontend = af9013_get_frontend,
+
+ .get_tune_settings = af9013_get_tune_settings,
+
+ .read_status = af9013_read_status,
+ .read_ber = af9013_read_ber,
+ .read_signal_strength = af9013_read_signal_strength,
+ .read_snr = af9013_read_snr,
+ .read_ucblocks = af9013_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/dvb/frontends/af9013.h b/linux/drivers/media/dvb/frontends/af9013.h
new file mode 100644
index 000000000..28b90c91c
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/af9013.h
@@ -0,0 +1,107 @@
+/*
+ * 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 _AF9013_H_
+#define _AF9013_H_
+
+#include <linux/dvb/frontend.h>
+
+enum af9013_ts_mode {
+ AF9013_OUTPUT_MODE_PARALLEL,
+ AF9013_OUTPUT_MODE_SERIAL,
+ AF9013_OUTPUT_MODE_USB, /* only for AF9015 */
+};
+
+enum af9013_tuner {
+ AF9013_TUNER_MXL5003D = 3, /* MaxLinear */
+ AF9013_TUNER_MXL5005D = 13, /* MaxLinear */
+ AF9013_TUNER_MXL5005R = 30, /* MaxLinear */
+ AF9013_TUNER_ENV77H11D5 = 129, /* Panasonic */
+ AF9013_TUNER_MT2060 = 130, /* Microtune */
+ AF9013_TUNER_MC44S803 = 133, /* Freescale */
+ AF9013_TUNER_QT1010 = 134, /* Quantek */
+ AF9013_TUNER_UNKNOWN = 140, /* for can tuners ? */
+ AF9013_TUNER_MT2060_2 = 147, /* Microtune */
+ AF9013_TUNER_TDA18271 = 156, /* NXP */
+ AF9013_TUNER_QT1010A = 162, /* Quantek */
+};
+
+/* AF9013/5 GPIOs (mostly guessed)
+ demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
+ demod#1-gpio#1 - xtal setting (?)
+ demod#1-gpio#3 - tuner#1
+ demod#2-gpio#0 - tuner#2
+ demod#2-gpio#1 - xtal setting (?)
+*/
+#define AF9013_GPIO_ON (1 << 0)
+#define AF9013_GPIO_EN (1 << 1)
+#define AF9013_GPIO_O (1 << 2)
+#define AF9013_GPIO_I (1 << 3)
+
+#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+
+#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+
+struct af9013_config {
+ /* demodulator's I2C address */
+ u8 demod_address;
+
+ /* frequencies in kHz */
+ u32 adc_clock;
+
+ /* tuner ID */
+ u8 tuner;
+
+ /* tuner IF */
+ u16 tuner_if;
+
+ /* TS data output mode */
+ u8 output_mode:2;
+
+ /* RF spectrum inversion */
+ u8 rf_spec_inv:1;
+
+ /* API version */
+ u8 api_version[4];
+
+ /* GPIOs */
+ u8 gpio[4];
+};
+
+
+#if defined(CONFIG_DVB_AF9013) || \
+ (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE))
+extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *af9013_attach(
+const struct af9013_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_AF9013 */
+
+#endif /* _AF9013_H_ */
diff --git a/linux/drivers/media/dvb/frontends/af9013_priv.h b/linux/drivers/media/dvb/frontends/af9013_priv.h
new file mode 100644
index 000000000..b62e4cc9b
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/af9013_priv.h
@@ -0,0 +1,869 @@
+/*
+ * 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 _AF9013_PRIV_
+#define _AF9013_PRIV_
+
+#define LOG_PREFIX "af9013"
+extern int debug;
+
+#define dprintk(var, level, args...) \
+ do { if ((var & level)) printk(args); } while (0)
+
+#define debug_dump(b, l, func) {\
+ int loop_; \
+ for (loop_ = 0; loop_ < l; loop_++) \
+ func("%02x ", b[loop_]); \
+ func("\n");\
+}
+
+#define deb_info(args...) dprintk(debug, 0x01, args)
+
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+#define AF9013_DEFAULT_FIRMWARE "dvb-fe-af9013.fw"
+
+struct regdesc {
+ u16 addr;
+ u8 pos:4;
+ u8 len:4;
+ u8 val;
+};
+
+struct snr_table {
+ u32 val;
+ u8 snr;
+};
+
+/* QPSK SNR lookup table */
+static struct snr_table qpsk_snr_table[] = {
+ { 0x0b4771, 0 },
+ { 0x0c1aed, 1 },
+ { 0x0d0d27, 2 },
+ { 0x0e4d19, 3 },
+ { 0x0e5da8, 4 },
+ { 0x107097, 5 },
+ { 0x116975, 6 },
+ { 0x1252d9, 7 },
+ { 0x131fa4, 8 },
+ { 0x13d5e1, 9 },
+ { 0x148e53, 10 },
+ { 0x15358b, 11 },
+ { 0x15dd29, 12 },
+ { 0x168112, 13 },
+ { 0x170b61, 14 },
+ { 0xffffff, 15 },
+};
+
+/* QAM16 SNR lookup table */
+static struct snr_table qam16_snr_table[] = {
+ { 0x05eb62, 5 },
+ { 0x05fecf, 6 },
+ { 0x060b80, 7 },
+ { 0x062501, 8 },
+ { 0x064865, 9 },
+ { 0x069604, 10 },
+ { 0x06f356, 11 },
+ { 0x07706a, 12 },
+ { 0x0804d3, 13 },
+ { 0x089d1a, 14 },
+ { 0x093e3d, 15 },
+ { 0x09e35d, 16 },
+ { 0x0a7c3c, 17 },
+ { 0x0afaf8, 18 },
+ { 0x0b719d, 19 },
+ { 0xffffff, 20 },
+};
+
+/* QAM64 SNR lookup table */
+static struct snr_table qam64_snr_table[] = {
+ { 0x03109b, 12 },
+ { 0x0310d4, 13 },
+ { 0x031920, 14 },
+ { 0x0322d0, 15 },
+ { 0x0339fc, 16 },
+ { 0x0364a1, 17 },
+ { 0x038bcc, 18 },
+ { 0x03c7d3, 19 },
+ { 0x0408cc, 20 },
+ { 0x043bed, 21 },
+ { 0x048061, 22 },
+ { 0x04be95, 23 },
+ { 0x04fa7d, 24 },
+ { 0x052405, 25 },
+ { 0x05570d, 26 },
+ { 0xffffff, 27 },
+};
+
+static struct regdesc ofsm_init[] = {
+ { 0xd73a, 0, 8, 0xa1 },
+ { 0xd73b, 0, 8, 0x1f },
+ { 0xd73c, 4, 4, 0x0a },
+ { 0xd732, 3, 1, 0x00 },
+ { 0xd731, 4, 2, 0x03 },
+ { 0xd73d, 7, 1, 0x01 },
+ { 0xd740, 0, 1, 0x00 },
+ { 0xd740, 1, 1, 0x00 },
+ { 0xd740, 2, 1, 0x00 },
+ { 0xd740, 3, 1, 0x01 },
+ { 0xd3c1, 4, 1, 0x01 },
+ { 0xd3a2, 0, 8, 0x00 },
+ { 0xd3a3, 0, 8, 0x04 },
+ { 0xd305, 0, 8, 0x32 },
+ { 0xd306, 0, 8, 0x10 },
+ { 0xd304, 0, 8, 0x04 },
+ { 0x9112, 0, 1, 0x01 },
+ { 0x911d, 0, 1, 0x01 },
+ { 0x911a, 0, 1, 0x01 },
+ { 0x911b, 0, 1, 0x01 },
+ { 0x9bce, 0, 4, 0x02 },
+ { 0x9116, 0, 1, 0x01 },
+ { 0x9bd1, 0, 1, 0x01 },
+ { 0xd2e0, 0, 8, 0xd0 },
+ { 0xd2e9, 0, 4, 0x0d },
+ { 0xd38c, 0, 8, 0xfc },
+ { 0xd38d, 0, 8, 0x00 },
+ { 0xd38e, 0, 8, 0x7e },
+ { 0xd38f, 0, 8, 0x00 },
+ { 0xd390, 0, 8, 0x2f },
+ { 0xd145, 4, 1, 0x01 },
+ { 0xd1a9, 4, 1, 0x01 },
+ { 0xd158, 5, 3, 0x01 },
+ { 0xd159, 0, 6, 0x06 },
+ { 0xd167, 0, 8, 0x00 },
+ { 0xd168, 0, 4, 0x07 },
+ { 0xd1c3, 5, 3, 0x00 },
+ { 0xd1c4, 0, 6, 0x00 },
+ { 0xd1c5, 0, 7, 0x10 },
+ { 0xd1c6, 0, 3, 0x02 },
+ { 0xd080, 2, 5, 0x03 },
+ { 0xd081, 4, 4, 0x09 },
+ { 0xd098, 4, 4, 0x0f },
+ { 0xd098, 0, 4, 0x03 },
+ { 0xdbc0, 3, 1, 0x01 },
+ { 0xdbc0, 4, 1, 0x01 },
+ { 0xdbc7, 0, 8, 0x08 },
+ { 0xdbc8, 4, 4, 0x00 },
+ { 0xdbc9, 0, 5, 0x01 },
+ { 0xd280, 0, 8, 0xe0 },
+ { 0xd281, 0, 8, 0xff },
+ { 0xd282, 0, 8, 0xff },
+ { 0xd283, 0, 8, 0xc3 },
+ { 0xd284, 0, 8, 0xff },
+ { 0xd285, 0, 4, 0x01 },
+ { 0xd0f0, 0, 7, 0x1a },
+ { 0xd0f1, 4, 1, 0x01 },
+ { 0xd0f2, 0, 8, 0x0c },
+ { 0xd103, 0, 4, 0x08 },
+ { 0xd0f8, 0, 7, 0x20 },
+ { 0xd111, 5, 1, 0x00 },
+ { 0xd111, 6, 1, 0x00 },
+ { 0x910b, 0, 8, 0x0a },
+ { 0x9115, 0, 8, 0x02 },
+ { 0x910c, 0, 8, 0x02 },
+ { 0x910d, 0, 8, 0x08 },
+ { 0x910e, 0, 8, 0x0a },
+ { 0x9bf6, 0, 8, 0x06 },
+ { 0x9bf8, 0, 8, 0x02 },
+ { 0x9bf7, 0, 8, 0x05 },
+ { 0x9bf9, 0, 8, 0x0f },
+ { 0x9bfc, 0, 8, 0x13 },
+ { 0x9bd3, 0, 8, 0xff },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+};
+
+/* Panasonic ENV77H11D5 tuner init
+ AF9013_TUNER_ENV77H11D5 = 129 */
+static struct regdesc tuner_init_env77h11d5[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x03 },
+ { 0x9bbe, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0xd015, 0, 8, 0x50 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0xeb },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf4 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0xeb },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0x52 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* Microtune MT2060 tuner init
+ AF9013_TUNER_MT2060 = 130 */
+static struct regdesc tuner_init_mt2060[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x07 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bbe, 0, 1, 0x00 },
+ { 0x9bcc, 0, 1, 0x00 },
+ { 0x9bb9, 0, 8, 0x75 },
+ { 0x9bcd, 0, 8, 0x24 },
+ { 0x9bff, 0, 8, 0x30 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x32 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x36 },
+ { 0xd00d, 0, 2, 0x03 },
+ { 0xd00a, 0, 8, 0x35 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x90 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x36 },
+ { 0x9bc6, 0, 8, 0x03 },
+ { 0x9bba, 0, 8, 0xc9 },
+ { 0x9bc9, 0, 8, 0x79 },
+ { 0xd011, 0, 8, 0x10 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0x45 },
+ { 0xd014, 0, 2, 0x03 },
+ { 0xd040, 0, 8, 0x98 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0xcf },
+ { 0xd043, 0, 2, 0x03 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xcc },
+ { 0x9be4, 0, 8, 0xa0 },
+ { 0x9bbd, 0, 8, 0x8e },
+ { 0x9be2, 0, 8, 0x4d },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Microtune MT2060 tuner init
+ AF9013_TUNER_MT2060_2 = 147 */
+static struct regdesc tuner_init_mt2060_2[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x06 },
+ { 0x9bbe, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x32 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x36 },
+ { 0xd00d, 0, 2, 0x03 },
+ { 0xd00a, 0, 8, 0x35 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x90 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x36 },
+ { 0x9bc6, 0, 8, 0x03 },
+ { 0x9bba, 0, 8, 0xc9 },
+ { 0x9bc9, 0, 8, 0x79 },
+ { 0xd011, 0, 8, 0x10 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0x45 },
+ { 0xd014, 0, 2, 0x03 },
+ { 0xd040, 0, 8, 0x98 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0xcf },
+ { 0xd043, 0, 2, 0x03 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 8, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x96 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0xd045, 7, 1, 0x00 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5003 tuner init
+ AF9013_TUNER_MXL5003D = 3 */
+static struct regdesc tuner_init_mxl5003d[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x09 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bfc, 0, 8, 0x0f },
+ { 0x9bf6, 0, 8, 0x01 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0xd015, 0, 8, 0x33 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x40 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x6c },
+ { 0xd007, 0, 2, 0x00 },
+ { 0xd00c, 0, 8, 0x3d },
+ { 0xd00d, 0, 2, 0x00 },
+ { 0xd00a, 0, 8, 0x45 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x52 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x3d },
+ { 0x9bc6, 0, 8, 0x00 },
+ { 0x9bba, 0, 8, 0xa2 },
+ { 0x9bc9, 0, 8, 0xa0 },
+ { 0xd011, 0, 8, 0x56 },
+ { 0xd012, 0, 2, 0x00 },
+ { 0xd013, 0, 8, 0x50 },
+ { 0xd014, 0, 2, 0x00 },
+ { 0xd040, 0, 8, 0x56 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0x50 },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 8, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5005 tuner init
+ AF9013_TUNER_MXL5005D = 13
+ AF9013_TUNER_MXL5005R = 30 */
+static struct regdesc tuner_init_mxl5005[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x07 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x28 },
+ { 0x9bff, 0, 8, 0x24 },
+ { 0xd015, 0, 8, 0x40 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x40 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x73 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0xfa },
+ { 0xd00d, 0, 2, 0x01 },
+ { 0xd00a, 0, 8, 0xff },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x23 },
+ { 0x9bc8, 0, 8, 0x55 },
+ { 0x9bc3, 0, 8, 0x01 },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0xfa },
+ { 0x9bc6, 0, 8, 0x01 },
+ { 0x9bba, 0, 8, 0xff },
+ { 0x9bc9, 0, 8, 0xff },
+ { 0x9bd3, 0, 8, 0x95 },
+ { 0xd011, 0, 8, 0x70 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xfb },
+ { 0xd014, 0, 2, 0x01 },
+ { 0xd040, 0, 8, 0x70 },
+ { 0xd041, 0, 2, 0x01 },
+ { 0xd042, 0, 8, 0xfb },
+ { 0xd043, 0, 2, 0x01 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0x93 },
+ { 0x9be4, 0, 8, 0xfe },
+ { 0x9bbd, 0, 8, 0x63 },
+ { 0x9be2, 0, 8, 0xfe },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Quantek QT1010 tuner init
+ AF9013_TUNER_QT1010 = 134
+ AF9013_TUNER_QT1010A = 162 */
+static struct regdesc tuner_init_qt1010[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x09 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x28 },
+ { 0x9bff, 0, 8, 0x20 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x99 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x0f },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0x50 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x00 },
+ { 0x9bc8, 0, 8, 0x00 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x0f },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bba, 0, 8, 0xc5 },
+ { 0x9bc9, 0, 8, 0xff },
+ { 0xd011, 0, 8, 0x58 },
+ { 0xd012, 0, 2, 0x02 },
+ { 0xd013, 0, 8, 0x89 },
+ { 0xd014, 0, 2, 0x01 },
+ { 0xd040, 0, 8, 0x58 },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x89 },
+ { 0xd043, 0, 2, 0x01 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xcd },
+ { 0x9be4, 0, 8, 0xbb },
+ { 0x9bbd, 0, 8, 0x93 },
+ { 0x9be2, 0, 8, 0x80 },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Freescale MC44S803 tuner init
+ AF9013_TUNER_MC44S803 = 133 */
+static struct regdesc tuner_init_mc44s803[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x06 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bf6, 0, 8, 0x01 },
+ { 0x9bf8, 0, 8, 0x02 },
+ { 0x9bf9, 0, 8, 0x02 },
+ { 0x9bfc, 0, 8, 0x1f },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x24 },
+ { 0x9bff, 0, 8, 0x24 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x01 },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x7b },
+ { 0xd007, 0, 2, 0x00 },
+ { 0xd00c, 0, 8, 0x7c },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xfe },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x08 },
+ { 0x9bc8, 0, 8, 0x9a },
+ { 0x9bc3, 0, 8, 0x01 },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x7c },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bba, 0, 8, 0xfc },
+ { 0x9bc9, 0, 8, 0xaa },
+ { 0xd011, 0, 8, 0x6b },
+ { 0xd012, 0, 2, 0x00 },
+ { 0xd013, 0, 8, 0x88 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x6b },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0x7c },
+ { 0xd043, 0, 2, 0x02 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0x9e },
+ { 0x9be4, 0, 8, 0xff },
+ { 0x9bbd, 0, 8, 0x9e },
+ { 0x9be2, 0, 8, 0x25 },
+ { 0x9bee, 0, 1, 0x01 },
+ { 0xd73b, 3, 1, 0x00 },
+};
+
+/* unknown, probably for tin can tuner, tuner init
+ AF9013_TUNER_UNKNOWN = 140 */
+static struct regdesc tuner_init_unknown[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x02 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x18 },
+ { 0x9bff, 0, 8, 0x2c },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x00 },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf6 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc8, 0, 8, 0xaa },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x00 },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0xf0 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* NXP TDA18271 tuner init
+ AF9013_TUNER_TDA18271 = 156 */
+static struct regdesc tuner_init_tda18271[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x04 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x18 },
+ { 0x9bff, 0, 8, 0x2c },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x00 },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf6 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc8, 0, 8, 0xaa },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x00 },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0xf0 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xa8 },
+ { 0x9be4, 0, 8, 0x7f },
+ { 0x9bbd, 0, 8, 0xa8 },
+ { 0x9be2, 0, 8, 0x20 },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+#endif /* _AF9013_PRIV_ */