From: Jos Hoekstra <joshoekstra@gmx.net>
To: linux-dvb@linuxtv.org
Cc: crope@iki.fi
Subject: [linux-dvb] Avermedia DVB-T Volar X
Date: Fri, 20 Jun 2008 15:31:10 +0200 [thread overview]
Message-ID: <485BB11E.6060204@gmx.net> (raw)
[-- Attachment #1: Type: text/plain, Size: 1043 bytes --]
This device has the following chips on it:
AF9015-NT*
MXL5003S
This device seems to work with the following additions to af9015.c in
the tree found under:
http://linuxtv.org/hg/~anttip/af9015-mxl500x/
Added under static struct usb_device_id af9015_usb_table[]:
/* AverMedia DVB-T Volar X) */
{USB_DEVICE(0x07ca, 0xa815)},
Added/ changed under static struct dvb_usb_device_properties
af9015_properties:
.num_device_descs = 8, /* to add another case, without it the change
didn't get picked up. */
and:
{
.name = " AverMedia DVB-T Volar X",
.cold_ids = {&af9015_usb_table[8], NULL},
.warm_ids = {NULL},
},
With these changes, which are in attached file as well, the normal make,
make install routine works and plugging the stick in to the usb-port it
gets recognized and firmware of:
http://www.otit.fi/~crope/v4l-dvb/af9015/af9015_firmware_cutter/firmware_files/4.95.0/
It can scan, tune and watch FTA-channels :)
Hope this helps and can be added to the official driver somehow?
Regards,
Jos Hoekstra
[-- Attachment #2: af9015.c --]
[-- Type: text/plain, Size: 29313 bytes --]
/*
* 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 "mxl500x.h"
/* debug */
int dvb_usb_af9015_debug = 0x3d;
module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
MODULE_PARM_DESC(
debug,
"set debugging level (1=info,xfer=2,rc=4,reg=8,i2c=16,fw=32 (or-able))."
DVB_USB_DEBUG_STATUS);
static struct af9013_config af9015_af9013_config[2] = {
{
.demod_address = AF9015_I2C_DEMOD,
.ts_mode = AF9013_USB,
}, {
.demod_address = 0x3a,
.ts_mode = AF9013_SERIAL_TS,
}
};
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 */
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->data0;
buf[6] = req->data1;
buf[7] = req->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_VIRTUAL_MEMORY:
case WRITE_MEMORY:
if (((req->addr & 0xff00) == 0xff00) ||
((req->addr & 0xae00) == 0xae00))
buf[0] = WRITE_VIRTUAL_MEMORY;
case COPY_FIRMWARE:
case DOWNLOAD_FIRMWARE:
break;
default:
err("%s: unknown command: %d", __func__, req->cmd);
}
/* write requested */
if (write) {
memcpy(&buf[8], req->data, req->len);
msg_len += req->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("%s: sending failed: %d (%d/%d)", __func__,
ret, msg_len, act_len);
else
if (act_len != msg_len)
return -1; /* all data is not send */
/* no ack for those packets */
if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
return 0;
/* receive ack and data if read req */
msg_len = 1 + 1 + req->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("%s: receiving failed: %d", __func__, ret);
return ret;
}
deb_xfer("<<< ");
debug_dump(buf, act_len, deb_xfer);
/* check status */
if (buf[1]) {
err("%s: command failed: %d", __func__, buf[1]);
return -1;
}
/* read request, copy returned data to return buf */
if (!write)
memcpy(req->data, &buf[2], req->len);
return 0;
}
static int af9015_rw(struct dvb_usb_device *d, struct req_t *req)
{
int ret;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
ret = af9015_rw_udev(d->udev, req);
mutex_unlock(&d->i2c_mutex);
return ret;
}
static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val)
{
struct req_t req = { WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, 1,
&val };
return af9015_rw(d, &req);
}
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_rw(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, 3, 1, &val };
return af9015_rw(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, 3, 1, val };
return af9015_rw(d, &req);
}
static int af9015_i2c_write_regs(struct dvb_usb_device *d, u8 addr, u8 reg,
u8 *val, u8 count)
{
struct req_t req = { WRITE_I2C, addr, reg, 0, 1, count, val };
return af9015_rw(d, &req);
}
#if 0
static int af9015_i2c_write_reg(struct dvb_usb_device *d, u8 addr, u8 reg,
u8 val)
{
return af9015_i2c_write_regs(d, addr, reg, &val, 1);
}
#endif
static int af9015_i2c_read_reg(struct dvb_usb_device *d, u8 addr, u8 reg,
u8 *val)
{
struct req_t req = { READ_I2C, addr, reg, 0, 1, 1, val };
return af9015_rw(d, &req);
}
#if 0
static int af9015_eeprom_write_reg(struct dvb_usb_device *d, u8 addr, u8 val)
{
return af9015_i2c_write_reg(d, AF9015_I2C_EEPROM, addr, val);
}
#endif
static int af9015_eeprom_read_reg(struct dvb_usb_device *d, u8 addr, u8 *val)
{
return af9015_i2c_read_reg(d, AF9015_I2C_EEPROM, addr, val);
}
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;
u16 addr;
#if 0
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
#endif
if (num > 2) {
warn("more than 2 i2c messages at a time is not handled yet");
return -EINVAL;
}
if (msg[0].addr == AF9015_I2C_DEMOD ||
msg[0].addr == 0x3a) { /* 0x38 is targeted to demod */
addr = msg[0].buf[0] << 8;
addr += msg[0].buf[1];
#if 1
if (msg[0].addr == AF9015_I2C_DEMOD) {
if (num == 2) {
/* reads a single register */
ret = af9015_read_reg(d, addr, &msg[1].buf[0]);
if (!ret)
ret = 2;
} else {
/* writes a single register */
ret = af9015_write_reg(d, addr, msg[0].buf[2]);
if (!ret)
ret = 1;
}
} else {
if (num == 2) {
/* reads a single register */
ret = af9015_read_reg_i2c(d, msg[0].addr, addr,
&msg[1].buf[0]);
if (!ret)
ret = 2;
} else {
/* writes a single register */
ret = af9015_write_reg_i2c(d, msg[0].addr, addr,
msg[0].buf[2]);
if (!ret)
ret = 1;
}
}
#else
if (num == 2) {
/* reads a single register */
ret = af9015_read_reg_i2c(d, msg[0].addr, addr,
&msg[1].buf[0]);
if (!ret)
ret = 2;
} else {
/* writes a single register */
ret = af9015_write_reg_i2c(d, msg[0].addr, addr,
msg[0].buf[2]);
if (!ret)
ret = 1;
}
#endif
} else { /* normal i2c request, for e.g. tuner */
if (num == 2) {
/* reads a single register */
ret = af9015_i2c_read_reg(d, msg[0].addr, *msg[0].buf,
msg[1].buf);
if (!ret)
ret = 2;
} else {
/* writes a single register */
ret = af9015_i2c_write_regs(d, msg[0].addr,
msg[0].buf[0], &msg[0].buf[1], msg[0].len-1);
if (!ret)
ret = 1;
}
}
#if 0
mutex_unlock(&d->i2c_mutex);
#endif
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;
struct af9015_state *state = d->priv;
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 exit;
ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */
if (ret)
goto exit;
ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */
if (ret)
goto exit;
ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */
if (ret)
goto exit;
ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */
if (ret)
goto exit;
if (state->dual_mode) {
ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */
if (ret)
goto exit;
}
ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */
if (ret)
goto exit;
if (state->dual_mode) {
ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */
if (ret)
goto exit;
}
/* EP4 xfer length */
ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff);
if (ret)
goto exit;
ret = af9015_write_reg(d, 0xdd89, frame_size >> 8);
if (ret)
goto exit;
/* EP5 xfer length */
ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff);
if (ret)
goto exit;
ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8);
if (ret)
goto exit;
ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */
if (ret)
goto exit;
ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */
if (ret)
goto exit;
ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */
if (ret)
goto exit;
if (state->dual_mode) {
ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */
if (ret)
goto exit;
}
/* enable / disable mp2if2 */
if (state->dual_mode)
ret = af9015_set_reg_bit(d, 0xd50b, 0);
else
ret = af9015_clear_reg_bit(d, 0xd50b, 0);
exit:
if (ret)
err("%s: failed:%d", __func__, ret);
return ret;
}
#define MERC_FW_DOWNLOAD_BASE 0x5100
#define MERC_FW_STATUS 0x98be
#define MERC_BOOT_REQUEST 0xe205
static int af9015_copy_firmware(struct dvb_usb_device *d)
{
int ret;
u8 buf[] = { 0x3e, 0x29, 0x98, 0x8d }; /* FW len & checksum FIXME */
u8 val;
struct req_t req = { COPY_FIRMWARE, 0, MERC_FW_DOWNLOAD_BASE, 0, 0,
sizeof(buf), buf };
deb_info("%s:\n", __func__);
/* check firmware status */
ret = af9015_read_reg_i2c(d, 0x3a, MERC_FW_STATUS, &val);
if (ret)
goto exit;
deb_info("%s: firmware status:%02x\n", __func__, val);
/* copy firmware */
/* set I2C master clock to fast */
ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */
if (ret)
goto exit;
msleep(50);
ret = af9015_rw(d, &req);
if (ret)
goto exit;
/* set I2C master clock back to normal */
ret = af9015_write_reg(d, 0xd416, 0x01); /* 0x14 * 400ns */
if (ret)
goto exit;
msleep(100);
/* request boot firmware */
ret = af9015_write_reg_i2c(d, 0x3a, MERC_BOOT_REQUEST, 1);
if (ret)
goto exit;
msleep(100);
/* check firmware status */
ret = af9015_read_reg_i2c(d, 0x3a, MERC_FW_STATUS, &val);
if (ret)
goto exit;
exit:
if (ret) {
err("%s: failed, err:%d", __func__, ret);
deb_info("%s: failed, err:%d\n", __func__, ret);
}
return ret;
}
/* dump eeprom */
static int af9015_eeprom_dump(struct dvb_usb_device *d)
{
char buf[52], buf2[4];
u8 reg, val;
deb_info("%s:\n", __func__);
for (reg = 0; ; reg++) {
if (reg % 16 == 0) {
if (reg)
deb_info("%s\n", buf);
sprintf(buf, "%02x: ", reg);
}
if (af9015_eeprom_read_reg(d, 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, ret;
u16 addr = 0x9a56; /* ir-table start address */
struct req_t req = { WRITE_MEMORY, AF9015_I2C_DEMOD, 0, 0, 0, 1, NULL };
deb_info("%s:\n", __func__);
packets = sizeof(ir_table);
for (i = 0; i < packets; i++) {
req.addr = addr + i;
req.data = &ir_table[i];
ret = af9015_rw(d, &req);
if (ret) {
err("%s: ir-table download failed at packet %d with " \
"code %d", __func__, i, ret);
return ret;
}
}
return 0;
}
static int af9015_set_gpio(struct dvb_usb_device *d, u8 gpio, u8 gpioval)
{
int ret;
u8 regval;
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("%s: invalid gpio:%d\n", __func__, gpio);
ret = -EINVAL;
goto exit;
};
ret = af9015_read_reg(d, addr, ®val);
if (ret)
goto exit;
switch (gpio) {
case 0:
case 2:
regval &= 0xf0;
regval |= (gpioval << 0);
break;
case 1:
case 3:
regval &= 0x0f;
regval |= (gpioval << 4);
break;
};
ret = af9015_write_reg(d, addr, regval);
exit:
if (ret)
err("%s: failed, err:%d", __func__, ret);
return ret;
}
static int af9015_init(struct dvb_usb_device *d)
{
int ret;
deb_info("%s:\n", __func__);
ret = af9015_init_endpoint(d);
if (ret)
goto exit;
ret = af9015_download_ir_table(d);
if (ret)
goto exit;
exit:
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 0
if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
return -EAGAIN;
#endif
if (onoff)
ret = af9015_set_reg_bit(adap->dev, 0xd503, 0);
else
ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0);
#if 0
mutex_unlock(&adap->dev->i2c_mutex);
#endif
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);
#if 0
if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
return -EAGAIN;
#endif
ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff));
if (ret)
goto exit;
ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8));
if (ret)
goto exit;
idx = ((index & 0x1f) | (1 << 5));
ret = af9015_write_reg(adap->dev, 0xd504, idx);
exit:
#if 0
mutex_unlock(&adap->dev->i2c_mutex);
#endif
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, AF9015_I2C_DEMOD, 0, 0, 0, 0,
NULL };
u16 addr = 0x5100; /* firmware start address */
u8 tmp;
deb_info("%s:\n", __func__);
#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.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("%s: firmware download failed at packet %d with " \
"code %d", __func__, i, ret);
goto exit;
}
}
#undef FW_PACKET_MAX_DATA
/* firmware loaded, request boot */
req.cmd = BOOT;
ret = af9015_rw_udev(udev, &req);
if (ret) {
err("%s: boot failed: %d", __func__, ret);
goto exit;
}
#if 0
msleep(1);
/* boot done, ensure that firmware is running */
req.cmd = GET_CONFIG;
req.len = 1;
req.data = &tmp;
ret = af9015_rw_udev(udev, &req);
if (ret) {
err("%s: get config failed: %d", __func__, ret);
goto exit;
}
if (tmp != 0x02) {
err("%s: firmware did not run (%02x)", __func__, tmp);
return -EIO;
}
#endif
#if 1
/* firmware is running, reconnect device in the usb bus */
req.cmd = RECONNECT_USB;
ret = af9015_rw_udev(udev, &req);
if (ret)
err("%s: reconnect failed: %d", __func__, ret);
#endif
exit:
return ret;
}
static int af9015_read_config(struct dvb_usb_device *d)
{
int ret;
struct af9015_state *state = d->priv;
u8 val;
#if 0
/* remote */
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_IR_MODE, &val);
if (ret)
goto exit;
deb_info("%s: ir mode:%d\n", __func__, val);
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_IR_REMOTE_TYPE, &val);
if (ret)
goto exit;
deb_info("%s: ir remote type:%d\n", __func__, val);
#endif
/* TS mode - one or two receivers */
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_TS_MODE, &val);
if (ret)
goto exit;
state->dual_mode = val;
deb_info("%s: TS mode:%d\n", __func__, val);
/* xtal */
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_XTAL_TYPE1, &val);
if (ret)
goto exit;
switch (val) {
case 0:
af9015_af9013_config[0].adc_clock = 28800;
af9015_af9013_config[1].adc_clock = 28800;
break;
case 1:
af9015_af9013_config[0].adc_clock = 20480;
af9015_af9013_config[1].adc_clock = 20480;
break;
case 2:
af9015_af9013_config[0].adc_clock = 28000;
af9015_af9013_config[1].adc_clock = 28000;
break;
case 3:
af9015_af9013_config[0].adc_clock = 25000;
af9015_af9013_config[1].adc_clock = 25000;
break;
};
deb_info("%s: xtal:%d set adc_clock:%d\n", __func__, val,
af9015_af9013_config[0].adc_clock);
/* tuner IF */
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_IF1H, &val);
if (ret)
goto exit;
af9015_af9013_config[0].tuner_if = val << 8;
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_IF1L, &val);
if (ret)
goto exit;
af9015_af9013_config[0].tuner_if += val;
deb_info("%s: IF1:%d\n", __func__, af9015_af9013_config[0].tuner_if);
af9015_af9013_config[1].tuner_if = af9015_af9013_config[0].tuner_if;
/* MT2060 IF1 */
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_MT2060_IF1H, &val);
if (ret)
goto exit;
state->mt2060_if1 = val << 8;
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_MT2060_IF1L, &val);
if (ret)
goto exit;
state->mt2060_if1 += val;
deb_info("%s: MT2060 IF1:%d\n", __func__, state->mt2060_if1);
/* tuner */
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_TUNER_ID1, &val);
if (ret)
goto exit;
switch (val) {
case AF9013_TUNER_MT2060:
case AF9013_TUNER_QT1010:
case AF9013_TUNER_TDA18271:
af9015_af9013_config[0].rf_spec_inv = 1;
break;
case AF9013_TUNER_MXL5003D:
case AF9013_TUNER_MXL5005D:
case AF9013_TUNER_MXL5005R:
af9015_af9013_config[0].rf_spec_inv = 0;
af9015_af9013_config[1].rf_spec_inv = 0;
break;
default:
warn("%s: tuner id:%d not supported, please report!",
__func__, val);
return -ENODEV;
};
af9015_af9013_config[0].tuner = val;
af9015_af9013_config[1].tuner = val;
deb_info("%s: tuner id1:%d\n", __func__, val);
/* spectral inversion */
ret = af9015_eeprom_read_reg(d, AF9015_EEPROM_SPEC_INV1, &val);
if (ret)
goto exit;
/* does this really have any meaning? looks like it is always 0...
af9015_af9013_config.rf_spec_inv = val; */
deb_info("%s: spectral inversion:%d\n", __func__, val);
exit:
if (ret)
err("%s: eeprom read failed, err:%d", __func__, 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, AF9015_I2C_DEMOD, 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;
}
/* A-Link DTU(m) */
static struct dvb_usb_rc_key af9015_rc_keys[] = {
{ 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 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;
#if 0
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
#endif
ret = af9015_rw(d, &req);
#if 0
mutex_unlock(&d->i2c_mutex);
#endif
if (ret)
return ret;
*event = 0;
*state = REMOTE_NO_KEY_PRESSED;
for (i = 0; i < d->props.rc_key_map_size; i++) {
if (keymap[i].custom == buf[0] &&
keymap[i].data == buf[2]) {
*event = keymap[i].event;
*state = REMOTE_KEY_PRESSED;
return 0;
}
}
return 0;
}
static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
{
int ret;
struct af9015_state *state = adap->dev->priv;
/* dump eeprom (debug) */
ret = af9015_eeprom_dump(adap->dev);
if (ret)
return ret;
/* read configuration from eeprom */
ret = af9015_read_config(adap->dev);
if (ret)
return ret;
/* copy firmware to 2nd demodulator */
if (state->dual_mode) {
ret = af9015_copy_firmware(adap->dev);
if (ret) {
info("%s: firmware copy to 2nd frontend failed, " \
"will disable it", __func__);
state->dual_mode = 0;
ret = 0;
}
}
/* attach demodulator */
adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[0],
&adap->dev->i2c_adap);
if (adap->fe != NULL)
return 0;
return -EIO;
}
static int af9015_af9013_frontend2_attach(struct dvb_usb_adapter *adap)
{
struct af9015_state *state = adap->dev->priv;
/* attach 2nd demodulator */
if (state->dual_mode) {
adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[1],
&adap->dev->i2c_adap);
if (adap->fe != NULL)
return 0;
}
return -EIO;
}
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 mxl500x_config af9015_mxl5003_config = {
.delsys = MXL500x_MODE_DVBT,
.octf = 0,
.xtal_freq = 16000000,
.iflo_freq = 4570000,
.ref_freq = 365600000,
.rssi_ena = 0,
.addr = 0xc6,
};
static struct mxl500x_config af9015_mxl5005_config = {
.delsys = MXL500x_MODE_DVBT,
.octf = MXL500x_OCTF_OFF,
.xtal_freq = 16000000,
.iflo_freq = 4570000,
.ref_freq = 365600000,
.rssi_ena = 0,
.addr = 0xc6,
};
static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
{
struct af9015_state *state = adap->dev->priv;
int ret = 0;
deb_info("%s: \n", __func__);
#if 0
/* connect tuner */
switch (af9015_af9013_config[0].tuner) {
case AF9013_TUNER_MT2060:
case AF9013_TUNER_QT1010:
case AF9013_TUNER_TDA18271:
ret = af9015_set_gpio(adap->dev, 3, AF9015_GPIO_TUNER_ON);
break;
case AF9013_TUNER_MXL5003D:
case AF9013_TUNER_MXL5005D:
case AF9013_TUNER_MXL5005R:
ret = af9015_set_gpio(adap->dev, 1, AF9015_GPIO_TUNER_ON);
if (state->dual_mode)
ret = af9015_set_gpio(adap->dev, 0,
AF9015_GPIO_TUNER_ON);
break;
}
if (ret)
return ret;
#endif
switch (af9015_af9013_config[0].tuner) {
case AF9013_TUNER_MT2060:
ret = dvb_attach(mt2060_attach,
adap->fe, &adap->dev->i2c_adap,
&af9015_mt2060_config,
state->mt2060_if1) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_QT1010:
ret = dvb_attach(qt1010_attach,
adap->fe, &adap->dev->i2c_adap,
&af9015_qt1010_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_TDA18271:
ret = dvb_attach(tda18271_attach,
adap->fe, 0xc0, &adap->dev->i2c_adap,
&af9015_tda18271_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5003D:
ret = dvb_attach(mxl500x_attach,
adap->fe,
&af9015_mxl5003_config,
&adap->dev->i2c_adap) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_MXL5005D:
case AF9013_TUNER_MXL5005R:
ret = dvb_attach(mxl500x_attach,
adap->fe,
&af9015_mxl5005_config,
&adap->dev->i2c_adap) == NULL ? -ENODEV : 0;
break;
default:
ret = -EINVAL;
err("%s: Unknown tuner id:%d", __func__,
af9015_af9013_config[0].tuner);
}
return ret;
}
static struct dvb_usb_device_properties af9015_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;
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 = dvb_usb_device_init(intf, &af9015_properties,
THIS_MODULE, &d);
if (ret)
return ret;
if (d)
ret = af9015_init(d);
}
return ret;
}
static struct usb_device_id af9015_usb_table[] = {
{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_PCTV_71E)},
/* KWorld PlusTV Dual DVB-T Stick (DVB-T 399U) */
{USB_DEVICE(0x1b80, 0xe399)},
{USB_DEVICE(USB_VID_VISIONPLUS, 0x3226)},
/* TwinHan AzureWave AD-TU700(704J) */
{USB_DEVICE(USB_VID_VISIONPLUS, 0x3237)},
/* TerraTec Cinergy T USB XE (Rev. 2) */
{USB_DEVICE(USB_VID_TERRATEC, 0x0069)},
/* AverMedia DVB-T Volar X) */
{USB_DEVICE(0x07ca, 0xa815)},
{0},
};
MODULE_DEVICE_TABLE(usb, af9015_usb_table);
static struct dvb_usb_device_properties af9015_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.i2c_algo = &af9015_i2c_algo,
.usb_ctrl = DEVICE_SPECIFIC,
.size_of_priv = sizeof(struct af9015_state),
#if 0
.rc_interval = 200,
.rc_key_map = af9015_rc_keys,
.rc_key_map_size = ARRAY_SIZE(af9015_rc_keys),
.rc_query = af9015_rc_query,
#endif
.identify_state = af9015_identify_state,
.firmware = "dvb-usb-af9015.fw",
.download_firmware = af9015_download_firmware,
.num_adapters = 2,
.adapter = {
{
#if 0
.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,
#endif
.frontend_attach = af9015_af9013_frontend_attach,
.tuner_attach = af9015_tuner_attach,
.stream = {
.type = USB_BULK,
.count = 6,
.endpoint = 0x84,
.u = {
.bulk = {
.buffersize =
TS_USB20_FRAME_SIZE,
}
}
},
},
{
#if 0
.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,
#endif
.frontend_attach = af9015_af9013_frontend2_attach,
.tuner_attach = af9015_tuner_attach,
.stream = {
.type = USB_BULK,
.count = 6,
.endpoint = 0x85,
.u = {
.bulk = {
.buffersize =
TS_USB20_FRAME_SIZE,
}
}
},
}
},
.num_device_descs = 8,
.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 = " AverMedia DVB-T Volar X",
.cold_ids = {&af9015_usb_table[8], NULL},
.warm_ids = {NULL},
},
{NULL},
}
};
/* 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 = dvb_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("%s: usb_register failed. Error number %d", __func__, 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");
[-- Attachment #3: Avermedia Volar X.txt --]
[-- Type: text/plain, Size: 4198 bytes --]
Stuff on Chips:
AF9015-NT*
0729 HKH2T
MXL5003S
D37t3.21
0732
Bus 007 Device 003: ID 07ca:a815 AVerMedia Technologies, Inc.
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0 (Defined at Interface level)
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x07ca AVerMedia Technologies, Inc.
idProduct 0xa815
bcdDevice 2.00
iManufacturer 1 AVerMedia
iProduct 2 A815
iSerial 3 300700301156000
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 71
bNumInterfaces 2
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 500mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 4
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x84 EP 4 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x85 EP 5 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 0 No Subclass
bInterfaceProtocol 1 Keyboard
iInterface 0
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.01
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 65
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 16
Device Qualifier (for other device speed):
bLength 10
bDescriptorType 6
bcdUSB 2.00
bDeviceClass 0 (Defined at Interface level)
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
bNumConfigurations 1
Device Status: 0x0000
(Bus Powered)
[-- Attachment #4: Type: text/plain, Size: 150 bytes --]
_______________________________________________
linux-dvb mailing list
linux-dvb@linuxtv.org
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb
next reply other threads:[~2008-06-20 13:31 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-06-20 13:31 Jos Hoekstra [this message]
2008-07-02 10:04 ` [linux-dvb] Avermedia DVB-T Volar X Antti Palosaari
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=485BB11E.6060204@gmx.net \
--to=joshoekstra@gmx.net \
--cc=crope@iki.fi \
--cc=linux-dvb@linuxtv.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.