xref: /linux/drivers/media/usb/dvb-usb/dtv5100.c (revision 4a307b4a)
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