forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
4.9 KiB
217 lines
4.9 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T |
|
* |
|
* Copyright (C) 2008 Antoine Jacquet <[email protected]> |
|
* http://royale.zerezo.com/dtv5100/ |
|
* |
|
* Inspired by gl861.c and au6610.c drivers |
|
*/ |
|
|
|
#include "dtv5100.h" |
|
#include "zl10353.h" |
|
#include "qt1010.h" |
|
|
|
/* debug */ |
|
static int dvb_usb_dtv5100_debug; |
|
module_param_named(debug, dvb_usb_dtv5100_debug, int, 0644); |
|
MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); |
|
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
|
|
|
struct dtv5100_state { |
|
unsigned char data[80]; |
|
}; |
|
|
|
static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr, |
|
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) |
|
{ |
|
struct dtv5100_state *st = d->priv; |
|
u8 request; |
|
u8 type; |
|
u16 value; |
|
u16 index; |
|
|
|
switch (wlen) { |
|
case 1: |
|
/* write { reg }, read { value } */ |
|
request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ : |
|
DTV5100_TUNER_READ); |
|
type = USB_TYPE_VENDOR | USB_DIR_IN; |
|
value = 0; |
|
break; |
|
case 2: |
|
/* write { reg, value } */ |
|
request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE : |
|
DTV5100_TUNER_WRITE); |
|
type = USB_TYPE_VENDOR | USB_DIR_OUT; |
|
value = wbuf[1]; |
|
break; |
|
default: |
|
warn("wlen = %x, aborting.", wlen); |
|
return -EINVAL; |
|
} |
|
index = (addr << 8) + wbuf[0]; |
|
|
|
memcpy(st->data, rbuf, rlen); |
|
msleep(1); /* avoid I2C errors */ |
|
return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), request, |
|
type, value, index, st->data, rlen, |
|
DTV5100_USB_TIMEOUT); |
|
} |
|
|
|
/* I2C */ |
|
static int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], |
|
int num) |
|
{ |
|
struct dvb_usb_device *d = i2c_get_adapdata(adap); |
|
int i; |
|
|
|
if (num > 2) |
|
return -EINVAL; |
|
|
|
if (mutex_lock_interruptible(&d->i2c_mutex) < 0) |
|
return -EAGAIN; |
|
|
|
for (i = 0; i < num; i++) { |
|
/* write/read request */ |
|
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { |
|
if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, |
|
msg[i].len, msg[i+1].buf, |
|
msg[i+1].len) < 0) |
|
break; |
|
i++; |
|
} else if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, |
|
msg[i].len, NULL, 0) < 0) |
|
break; |
|
} |
|
|
|
mutex_unlock(&d->i2c_mutex); |
|
return i; |
|
} |
|
|
|
static u32 dtv5100_i2c_func(struct i2c_adapter *adapter) |
|
{ |
|
return I2C_FUNC_I2C; |
|
} |
|
|
|
static struct i2c_algorithm dtv5100_i2c_algo = { |
|
.master_xfer = dtv5100_i2c_xfer, |
|
.functionality = dtv5100_i2c_func, |
|
}; |
|
|
|
/* Callbacks for DVB USB */ |
|
static struct zl10353_config dtv5100_zl10353_config = { |
|
.demod_address = DTV5100_DEMOD_ADDR, |
|
.no_tuner = 1, |
|
.parallel_ts = 1, |
|
}; |
|
|
|
static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap) |
|
{ |
|
adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config, |
|
&adap->dev->i2c_adap); |
|
if (adap->fe_adap[0].fe == NULL) |
|
return -EIO; |
|
|
|
/* disable i2c gate, or it won't work... is this safe? */ |
|
adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL; |
|
|
|
return 0; |
|
} |
|
|
|
static struct qt1010_config dtv5100_qt1010_config = { |
|
.i2c_address = DTV5100_TUNER_ADDR |
|
}; |
|
|
|
static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap) |
|
{ |
|
return dvb_attach(qt1010_attach, |
|
adap->fe_adap[0].fe, &adap->dev->i2c_adap, |
|
&dtv5100_qt1010_config) == NULL ? -ENODEV : 0; |
|
} |
|
|
|
/* DVB USB Driver stuff */ |
|
static struct dvb_usb_device_properties dtv5100_properties; |
|
|
|
static int dtv5100_probe(struct usb_interface *intf, |
|
const struct usb_device_id *id) |
|
{ |
|
int i, ret; |
|
struct usb_device *udev = interface_to_usbdev(intf); |
|
|
|
/* initialize non qt1010/zl10353 part? */ |
|
for (i = 0; dtv5100_init[i].request; i++) { |
|
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), |
|
dtv5100_init[i].request, |
|
USB_TYPE_VENDOR | USB_DIR_OUT, |
|
dtv5100_init[i].value, |
|
dtv5100_init[i].index, NULL, 0, |
|
DTV5100_USB_TIMEOUT); |
|
if (ret) |
|
return ret; |
|
} |
|
|
|
ret = dvb_usb_device_init(intf, &dtv5100_properties, |
|
THIS_MODULE, NULL, adapter_nr); |
|
if (ret) |
|
return ret; |
|
|
|
return 0; |
|
} |
|
|
|
static struct usb_device_id dtv5100_table[] = { |
|
{ USB_DEVICE(0x06be, 0xa232) }, |
|
{ } /* Terminating entry */ |
|
}; |
|
MODULE_DEVICE_TABLE(usb, dtv5100_table); |
|
|
|
static struct dvb_usb_device_properties dtv5100_properties = { |
|
.caps = DVB_USB_IS_AN_I2C_ADAPTER, |
|
.usb_ctrl = DEVICE_SPECIFIC, |
|
|
|
.size_of_priv = sizeof(struct dtv5100_state), |
|
|
|
.num_adapters = 1, |
|
.adapter = {{ |
|
.num_frontends = 1, |
|
.fe = {{ |
|
.frontend_attach = dtv5100_frontend_attach, |
|
.tuner_attach = dtv5100_tuner_attach, |
|
|
|
.stream = { |
|
.type = USB_BULK, |
|
.count = 8, |
|
.endpoint = 0x82, |
|
.u = { |
|
.bulk = { |
|
.buffersize = 4096, |
|
} |
|
} |
|
}, |
|
}}, |
|
} }, |
|
|
|
.i2c_algo = &dtv5100_i2c_algo, |
|
|
|
.num_device_descs = 1, |
|
.devices = { |
|
{ |
|
.name = "AME DTV-5100 USB2.0 DVB-T", |
|
.cold_ids = { NULL }, |
|
.warm_ids = { &dtv5100_table[0], NULL }, |
|
}, |
|
} |
|
}; |
|
|
|
static struct usb_driver dtv5100_driver = { |
|
.name = "dvb_usb_dtv5100", |
|
.probe = dtv5100_probe, |
|
.disconnect = dvb_usb_device_exit, |
|
.id_table = dtv5100_table, |
|
}; |
|
|
|
module_usb_driver(dtv5100_driver); |
|
|
|
MODULE_AUTHOR(DRIVER_AUTHOR); |
|
MODULE_DESCRIPTION(DRIVER_DESC); |
|
MODULE_LICENSE("GPL");
|
|
|