1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2786baecfSMauro Carvalho Chehab /*
3786baecfSMauro Carvalho Chehab * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T
4786baecfSMauro Carvalho Chehab *
5786baecfSMauro Carvalho Chehab * Copyright (C) 2008 Antoine Jacquet <royale@zerezo.com>
6786baecfSMauro Carvalho Chehab * http://royale.zerezo.com/dtv5100/
7786baecfSMauro Carvalho Chehab *
8786baecfSMauro Carvalho Chehab * Inspired by gl861.c and au6610.c drivers
9786baecfSMauro Carvalho Chehab */
10786baecfSMauro Carvalho Chehab
11786baecfSMauro Carvalho Chehab #include "dtv5100.h"
12786baecfSMauro Carvalho Chehab #include "zl10353.h"
13786baecfSMauro Carvalho Chehab #include "qt1010.h"
14786baecfSMauro Carvalho Chehab
15786baecfSMauro Carvalho Chehab /* debug */
16786baecfSMauro Carvalho Chehab static int dvb_usb_dtv5100_debug;
17786baecfSMauro Carvalho Chehab module_param_named(debug, dvb_usb_dtv5100_debug, int, 0644);
18786baecfSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
19786baecfSMauro Carvalho Chehab DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
20786baecfSMauro Carvalho Chehab
218f306145SMauro Carvalho Chehab struct dtv5100_state {
228f306145SMauro Carvalho Chehab unsigned char data[80];
238f306145SMauro Carvalho Chehab };
248f306145SMauro Carvalho Chehab
dtv5100_i2c_msg(struct dvb_usb_device * d,u8 addr,u8 * wbuf,u16 wlen,u8 * rbuf,u16 rlen)25786baecfSMauro Carvalho Chehab static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr,
26786baecfSMauro Carvalho Chehab u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
27786baecfSMauro Carvalho Chehab {
288f306145SMauro Carvalho Chehab struct dtv5100_state *st = d->priv;
298c8b9a9bSJohan Hovold unsigned int pipe;
30786baecfSMauro Carvalho Chehab u8 request;
31786baecfSMauro Carvalho Chehab u8 type;
32786baecfSMauro Carvalho Chehab u16 value;
33786baecfSMauro Carvalho Chehab u16 index;
34786baecfSMauro Carvalho Chehab
35786baecfSMauro Carvalho Chehab switch (wlen) {
36786baecfSMauro Carvalho Chehab case 1:
37786baecfSMauro Carvalho Chehab /* write { reg }, read { value } */
388c8b9a9bSJohan Hovold pipe = usb_rcvctrlpipe(d->udev, 0);
39786baecfSMauro Carvalho Chehab request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ :
40786baecfSMauro Carvalho Chehab DTV5100_TUNER_READ);
41786baecfSMauro Carvalho Chehab type = USB_TYPE_VENDOR | USB_DIR_IN;
42786baecfSMauro Carvalho Chehab value = 0;
43786baecfSMauro Carvalho Chehab break;
44786baecfSMauro Carvalho Chehab case 2:
45786baecfSMauro Carvalho Chehab /* write { reg, value } */
468c8b9a9bSJohan Hovold pipe = usb_sndctrlpipe(d->udev, 0);
47786baecfSMauro Carvalho Chehab request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE :
48786baecfSMauro Carvalho Chehab DTV5100_TUNER_WRITE);
49786baecfSMauro Carvalho Chehab type = USB_TYPE_VENDOR | USB_DIR_OUT;
50786baecfSMauro Carvalho Chehab value = wbuf[1];
51786baecfSMauro Carvalho Chehab break;
52786baecfSMauro Carvalho Chehab default:
53786baecfSMauro Carvalho Chehab warn("wlen = %x, aborting.", wlen);
54786baecfSMauro Carvalho Chehab return -EINVAL;
55786baecfSMauro Carvalho Chehab }
56786baecfSMauro Carvalho Chehab index = (addr << 8) + wbuf[0];
57786baecfSMauro Carvalho Chehab
588f306145SMauro Carvalho Chehab memcpy(st->data, rbuf, rlen);
59786baecfSMauro Carvalho Chehab msleep(1); /* avoid I2C errors */
608c8b9a9bSJohan Hovold return usb_control_msg(d->udev, pipe, request,
618f306145SMauro Carvalho Chehab type, value, index, st->data, rlen,
62786baecfSMauro Carvalho Chehab DTV5100_USB_TIMEOUT);
63786baecfSMauro Carvalho Chehab }
64786baecfSMauro Carvalho Chehab
65786baecfSMauro Carvalho Chehab /* I2C */
dtv5100_i2c_xfer(struct i2c_adapter * adap,struct i2c_msg msg[],int num)66786baecfSMauro Carvalho Chehab static int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
67786baecfSMauro Carvalho Chehab int num)
68786baecfSMauro Carvalho Chehab {
69786baecfSMauro Carvalho Chehab struct dvb_usb_device *d = i2c_get_adapdata(adap);
70786baecfSMauro Carvalho Chehab int i;
71786baecfSMauro Carvalho Chehab
72786baecfSMauro Carvalho Chehab if (num > 2)
73786baecfSMauro Carvalho Chehab return -EINVAL;
74786baecfSMauro Carvalho Chehab
75786baecfSMauro Carvalho Chehab if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
76786baecfSMauro Carvalho Chehab return -EAGAIN;
77786baecfSMauro Carvalho Chehab
78786baecfSMauro Carvalho Chehab for (i = 0; i < num; i++) {
79786baecfSMauro Carvalho Chehab /* write/read request */
80786baecfSMauro Carvalho Chehab if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
81786baecfSMauro Carvalho Chehab if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf,
82786baecfSMauro Carvalho Chehab msg[i].len, msg[i+1].buf,
83786baecfSMauro Carvalho Chehab msg[i+1].len) < 0)
84786baecfSMauro Carvalho Chehab break;
85786baecfSMauro Carvalho Chehab i++;
86786baecfSMauro Carvalho Chehab } else if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf,
87786baecfSMauro Carvalho Chehab msg[i].len, NULL, 0) < 0)
88786baecfSMauro Carvalho Chehab break;
89786baecfSMauro Carvalho Chehab }
90786baecfSMauro Carvalho Chehab
91786baecfSMauro Carvalho Chehab mutex_unlock(&d->i2c_mutex);
92786baecfSMauro Carvalho Chehab return i;
93786baecfSMauro Carvalho Chehab }
94786baecfSMauro Carvalho Chehab
dtv5100_i2c_func(struct i2c_adapter * adapter)95786baecfSMauro Carvalho Chehab static u32 dtv5100_i2c_func(struct i2c_adapter *adapter)
96786baecfSMauro Carvalho Chehab {
97786baecfSMauro Carvalho Chehab return I2C_FUNC_I2C;
98786baecfSMauro Carvalho Chehab }
99786baecfSMauro Carvalho Chehab
100786baecfSMauro Carvalho Chehab static struct i2c_algorithm dtv5100_i2c_algo = {
101786baecfSMauro Carvalho Chehab .master_xfer = dtv5100_i2c_xfer,
102786baecfSMauro Carvalho Chehab .functionality = dtv5100_i2c_func,
103786baecfSMauro Carvalho Chehab };
104786baecfSMauro Carvalho Chehab
105786baecfSMauro Carvalho Chehab /* Callbacks for DVB USB */
106786baecfSMauro Carvalho Chehab static struct zl10353_config dtv5100_zl10353_config = {
107786baecfSMauro Carvalho Chehab .demod_address = DTV5100_DEMOD_ADDR,
108786baecfSMauro Carvalho Chehab .no_tuner = 1,
109786baecfSMauro Carvalho Chehab .parallel_ts = 1,
110786baecfSMauro Carvalho Chehab };
111786baecfSMauro Carvalho Chehab
dtv5100_frontend_attach(struct dvb_usb_adapter * adap)112786baecfSMauro Carvalho Chehab static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap)
113786baecfSMauro Carvalho Chehab {
114786baecfSMauro Carvalho Chehab adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config,
115786baecfSMauro Carvalho Chehab &adap->dev->i2c_adap);
116786baecfSMauro Carvalho Chehab if (adap->fe_adap[0].fe == NULL)
117786baecfSMauro Carvalho Chehab return -EIO;
118786baecfSMauro Carvalho Chehab
119786baecfSMauro Carvalho Chehab /* disable i2c gate, or it won't work... is this safe? */
120786baecfSMauro Carvalho Chehab adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL;
121786baecfSMauro Carvalho Chehab
122786baecfSMauro Carvalho Chehab return 0;
123786baecfSMauro Carvalho Chehab }
124786baecfSMauro Carvalho Chehab
125786baecfSMauro Carvalho Chehab static struct qt1010_config dtv5100_qt1010_config = {
126786baecfSMauro Carvalho Chehab .i2c_address = DTV5100_TUNER_ADDR
127786baecfSMauro Carvalho Chehab };
128786baecfSMauro Carvalho Chehab
dtv5100_tuner_attach(struct dvb_usb_adapter * adap)129786baecfSMauro Carvalho Chehab static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap)
130786baecfSMauro Carvalho Chehab {
131786baecfSMauro Carvalho Chehab return dvb_attach(qt1010_attach,
132786baecfSMauro Carvalho Chehab adap->fe_adap[0].fe, &adap->dev->i2c_adap,
133786baecfSMauro Carvalho Chehab &dtv5100_qt1010_config) == NULL ? -ENODEV : 0;
134786baecfSMauro Carvalho Chehab }
135786baecfSMauro Carvalho Chehab
136786baecfSMauro Carvalho Chehab /* DVB USB Driver stuff */
137786baecfSMauro Carvalho Chehab static struct dvb_usb_device_properties dtv5100_properties;
138786baecfSMauro Carvalho Chehab
dtv5100_probe(struct usb_interface * intf,const struct usb_device_id * id)139786baecfSMauro Carvalho Chehab static int dtv5100_probe(struct usb_interface *intf,
140786baecfSMauro Carvalho Chehab const struct usb_device_id *id)
141786baecfSMauro Carvalho Chehab {
142786baecfSMauro Carvalho Chehab int i, ret;
143786baecfSMauro Carvalho Chehab struct usb_device *udev = interface_to_usbdev(intf);
144786baecfSMauro Carvalho Chehab
145786baecfSMauro Carvalho Chehab /* initialize non qt1010/zl10353 part? */
146786baecfSMauro Carvalho Chehab for (i = 0; dtv5100_init[i].request; i++) {
1478c8b9a9bSJohan Hovold ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
148786baecfSMauro Carvalho Chehab dtv5100_init[i].request,
149786baecfSMauro Carvalho Chehab USB_TYPE_VENDOR | USB_DIR_OUT,
150786baecfSMauro Carvalho Chehab dtv5100_init[i].value,
151786baecfSMauro Carvalho Chehab dtv5100_init[i].index, NULL, 0,
152786baecfSMauro Carvalho Chehab DTV5100_USB_TIMEOUT);
153786baecfSMauro Carvalho Chehab if (ret)
154786baecfSMauro Carvalho Chehab return ret;
155786baecfSMauro Carvalho Chehab }
156786baecfSMauro Carvalho Chehab
157786baecfSMauro Carvalho Chehab ret = dvb_usb_device_init(intf, &dtv5100_properties,
158786baecfSMauro Carvalho Chehab THIS_MODULE, NULL, adapter_nr);
159786baecfSMauro Carvalho Chehab if (ret)
160786baecfSMauro Carvalho Chehab return ret;
161786baecfSMauro Carvalho Chehab
162786baecfSMauro Carvalho Chehab return 0;
163786baecfSMauro Carvalho Chehab }
164786baecfSMauro Carvalho Chehab
1654a307b4aSMauro Carvalho Chehab enum {
1664a307b4aSMauro Carvalho Chehab AME_DTV5100,
167786baecfSMauro Carvalho Chehab };
1684a307b4aSMauro Carvalho Chehab
1694a307b4aSMauro Carvalho Chehab static struct usb_device_id dtv5100_table[] = {
1704a307b4aSMauro Carvalho Chehab DVB_USB_DEV(AME, AME_DTV5100),
1714a307b4aSMauro Carvalho Chehab { }
1724a307b4aSMauro Carvalho Chehab };
1734a307b4aSMauro Carvalho Chehab
174786baecfSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, dtv5100_table);
175786baecfSMauro Carvalho Chehab
176786baecfSMauro Carvalho Chehab static struct dvb_usb_device_properties dtv5100_properties = {
177786baecfSMauro Carvalho Chehab .caps = DVB_USB_IS_AN_I2C_ADAPTER,
178786baecfSMauro Carvalho Chehab .usb_ctrl = DEVICE_SPECIFIC,
179786baecfSMauro Carvalho Chehab
1808f306145SMauro Carvalho Chehab .size_of_priv = sizeof(struct dtv5100_state),
181786baecfSMauro Carvalho Chehab
182786baecfSMauro Carvalho Chehab .num_adapters = 1,
183786baecfSMauro Carvalho Chehab .adapter = {{
184786baecfSMauro Carvalho Chehab .num_frontends = 1,
185786baecfSMauro Carvalho Chehab .fe = {{
186786baecfSMauro Carvalho Chehab .frontend_attach = dtv5100_frontend_attach,
187786baecfSMauro Carvalho Chehab .tuner_attach = dtv5100_tuner_attach,
188786baecfSMauro Carvalho Chehab
189786baecfSMauro Carvalho Chehab .stream = {
190786baecfSMauro Carvalho Chehab .type = USB_BULK,
191786baecfSMauro Carvalho Chehab .count = 8,
192786baecfSMauro Carvalho Chehab .endpoint = 0x82,
193786baecfSMauro Carvalho Chehab .u = {
194786baecfSMauro Carvalho Chehab .bulk = {
195786baecfSMauro Carvalho Chehab .buffersize = 4096,
196786baecfSMauro Carvalho Chehab }
197786baecfSMauro Carvalho Chehab }
198786baecfSMauro Carvalho Chehab },
199786baecfSMauro Carvalho Chehab }},
200786baecfSMauro Carvalho Chehab } },
201786baecfSMauro Carvalho Chehab
202786baecfSMauro Carvalho Chehab .i2c_algo = &dtv5100_i2c_algo,
203786baecfSMauro Carvalho Chehab
204786baecfSMauro Carvalho Chehab .num_device_descs = 1,
205786baecfSMauro Carvalho Chehab .devices = {
206786baecfSMauro Carvalho Chehab {
207786baecfSMauro Carvalho Chehab .name = "AME DTV-5100 USB2.0 DVB-T",
208786baecfSMauro Carvalho Chehab .cold_ids = { NULL },
2094a307b4aSMauro Carvalho Chehab .warm_ids = { &dtv5100_table[AME_DTV5100], NULL },
210786baecfSMauro Carvalho Chehab },
211786baecfSMauro Carvalho Chehab }
212786baecfSMauro Carvalho Chehab };
213786baecfSMauro Carvalho Chehab
214786baecfSMauro Carvalho Chehab static struct usb_driver dtv5100_driver = {
215786baecfSMauro Carvalho Chehab .name = "dvb_usb_dtv5100",
216786baecfSMauro Carvalho Chehab .probe = dtv5100_probe,
217786baecfSMauro Carvalho Chehab .disconnect = dvb_usb_device_exit,
218786baecfSMauro Carvalho Chehab .id_table = dtv5100_table,
219786baecfSMauro Carvalho Chehab };
220786baecfSMauro Carvalho Chehab
221786baecfSMauro Carvalho Chehab module_usb_driver(dtv5100_driver);
222786baecfSMauro Carvalho Chehab
223786baecfSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR);
224786baecfSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC);
225786baecfSMauro Carvalho Chehab MODULE_LICENSE("GPL");
226