162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2008  Antoine Jacquet <royale@zerezo.com>
662306a36Sopenharmony_ci * http://royale.zerezo.com/dtv5100/
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Inspired by gl861.c and au6610.c drivers
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "dtv5100.h"
1262306a36Sopenharmony_ci#include "zl10353.h"
1362306a36Sopenharmony_ci#include "qt1010.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* debug */
1662306a36Sopenharmony_cistatic int dvb_usb_dtv5100_debug;
1762306a36Sopenharmony_cimodule_param_named(debug, dvb_usb_dtv5100_debug, int, 0644);
1862306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
1962306a36Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct dtv5100_state {
2262306a36Sopenharmony_ci	unsigned char data[80];
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr,
2662306a36Sopenharmony_ci			   u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct dtv5100_state *st = d->priv;
2962306a36Sopenharmony_ci	unsigned int pipe;
3062306a36Sopenharmony_ci	u8 request;
3162306a36Sopenharmony_ci	u8 type;
3262306a36Sopenharmony_ci	u16 value;
3362306a36Sopenharmony_ci	u16 index;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	switch (wlen) {
3662306a36Sopenharmony_ci	case 1:
3762306a36Sopenharmony_ci		/* write { reg }, read { value } */
3862306a36Sopenharmony_ci		pipe = usb_rcvctrlpipe(d->udev, 0);
3962306a36Sopenharmony_ci		request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ :
4062306a36Sopenharmony_ci							DTV5100_TUNER_READ);
4162306a36Sopenharmony_ci		type = USB_TYPE_VENDOR | USB_DIR_IN;
4262306a36Sopenharmony_ci		value = 0;
4362306a36Sopenharmony_ci		break;
4462306a36Sopenharmony_ci	case 2:
4562306a36Sopenharmony_ci		/* write { reg, value } */
4662306a36Sopenharmony_ci		pipe = usb_sndctrlpipe(d->udev, 0);
4762306a36Sopenharmony_ci		request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE :
4862306a36Sopenharmony_ci							DTV5100_TUNER_WRITE);
4962306a36Sopenharmony_ci		type = USB_TYPE_VENDOR | USB_DIR_OUT;
5062306a36Sopenharmony_ci		value = wbuf[1];
5162306a36Sopenharmony_ci		break;
5262306a36Sopenharmony_ci	default:
5362306a36Sopenharmony_ci		warn("wlen = %x, aborting.", wlen);
5462306a36Sopenharmony_ci		return -EINVAL;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci	index = (addr << 8) + wbuf[0];
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	memcpy(st->data, rbuf, rlen);
5962306a36Sopenharmony_ci	msleep(1); /* avoid I2C errors */
6062306a36Sopenharmony_ci	return usb_control_msg(d->udev, pipe, request,
6162306a36Sopenharmony_ci			       type, value, index, st->data, rlen,
6262306a36Sopenharmony_ci			       DTV5100_USB_TIMEOUT);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* I2C */
6662306a36Sopenharmony_cistatic int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
6762306a36Sopenharmony_ci			    int num)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct dvb_usb_device *d = i2c_get_adapdata(adap);
7062306a36Sopenharmony_ci	int i;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (num > 2)
7362306a36Sopenharmony_ci		return -EINVAL;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
7662306a36Sopenharmony_ci		return -EAGAIN;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
7962306a36Sopenharmony_ci		/* write/read request */
8062306a36Sopenharmony_ci		if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
8162306a36Sopenharmony_ci			if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf,
8262306a36Sopenharmony_ci					    msg[i].len, msg[i+1].buf,
8362306a36Sopenharmony_ci					    msg[i+1].len) < 0)
8462306a36Sopenharmony_ci				break;
8562306a36Sopenharmony_ci			i++;
8662306a36Sopenharmony_ci		} else if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf,
8762306a36Sopenharmony_ci					   msg[i].len, NULL, 0) < 0)
8862306a36Sopenharmony_ci				break;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	mutex_unlock(&d->i2c_mutex);
9262306a36Sopenharmony_ci	return i;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic u32 dtv5100_i2c_func(struct i2c_adapter *adapter)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	return I2C_FUNC_I2C;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic struct i2c_algorithm dtv5100_i2c_algo = {
10162306a36Sopenharmony_ci	.master_xfer   = dtv5100_i2c_xfer,
10262306a36Sopenharmony_ci	.functionality = dtv5100_i2c_func,
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* Callbacks for DVB USB */
10662306a36Sopenharmony_cistatic struct zl10353_config dtv5100_zl10353_config = {
10762306a36Sopenharmony_ci	.demod_address = DTV5100_DEMOD_ADDR,
10862306a36Sopenharmony_ci	.no_tuner = 1,
10962306a36Sopenharmony_ci	.parallel_ts = 1,
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int dtv5100_frontend_attach(struct dvb_usb_adapter *adap)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config,
11562306a36Sopenharmony_ci			      &adap->dev->i2c_adap);
11662306a36Sopenharmony_ci	if (adap->fe_adap[0].fe == NULL)
11762306a36Sopenharmony_ci		return -EIO;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* disable i2c gate, or it won't work... is this safe? */
12062306a36Sopenharmony_ci	adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic struct qt1010_config dtv5100_qt1010_config = {
12662306a36Sopenharmony_ci	.i2c_address = DTV5100_TUNER_ADDR
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int dtv5100_tuner_attach(struct dvb_usb_adapter *adap)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	return dvb_attach(qt1010_attach,
13262306a36Sopenharmony_ci			  adap->fe_adap[0].fe, &adap->dev->i2c_adap,
13362306a36Sopenharmony_ci			  &dtv5100_qt1010_config) == NULL ? -ENODEV : 0;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/* DVB USB Driver stuff */
13762306a36Sopenharmony_cistatic struct dvb_usb_device_properties dtv5100_properties;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int dtv5100_probe(struct usb_interface *intf,
14062306a36Sopenharmony_ci			 const struct usb_device_id *id)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	int i, ret;
14362306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* initialize non qt1010/zl10353 part? */
14662306a36Sopenharmony_ci	for (i = 0; dtv5100_init[i].request; i++) {
14762306a36Sopenharmony_ci		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
14862306a36Sopenharmony_ci				      dtv5100_init[i].request,
14962306a36Sopenharmony_ci				      USB_TYPE_VENDOR | USB_DIR_OUT,
15062306a36Sopenharmony_ci				      dtv5100_init[i].value,
15162306a36Sopenharmony_ci				      dtv5100_init[i].index, NULL, 0,
15262306a36Sopenharmony_ci				      DTV5100_USB_TIMEOUT);
15362306a36Sopenharmony_ci		if (ret)
15462306a36Sopenharmony_ci			return ret;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	ret = dvb_usb_device_init(intf, &dtv5100_properties,
15862306a36Sopenharmony_ci				  THIS_MODULE, NULL, adapter_nr);
15962306a36Sopenharmony_ci	if (ret)
16062306a36Sopenharmony_ci		return ret;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cienum {
16662306a36Sopenharmony_ci	AME_DTV5100,
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic struct usb_device_id dtv5100_table[] = {
17062306a36Sopenharmony_ci	DVB_USB_DEV(AME, AME_DTV5100),
17162306a36Sopenharmony_ci	{ }
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, dtv5100_table);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic struct dvb_usb_device_properties dtv5100_properties = {
17762306a36Sopenharmony_ci	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
17862306a36Sopenharmony_ci	.usb_ctrl = DEVICE_SPECIFIC,
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	.size_of_priv = sizeof(struct dtv5100_state),
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	.num_adapters = 1,
18362306a36Sopenharmony_ci	.adapter = {{
18462306a36Sopenharmony_ci		.num_frontends = 1,
18562306a36Sopenharmony_ci		.fe = {{
18662306a36Sopenharmony_ci		.frontend_attach = dtv5100_frontend_attach,
18762306a36Sopenharmony_ci		.tuner_attach    = dtv5100_tuner_attach,
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		.stream = {
19062306a36Sopenharmony_ci			.type = USB_BULK,
19162306a36Sopenharmony_ci			.count = 8,
19262306a36Sopenharmony_ci			.endpoint = 0x82,
19362306a36Sopenharmony_ci			.u = {
19462306a36Sopenharmony_ci				.bulk = {
19562306a36Sopenharmony_ci					.buffersize = 4096,
19662306a36Sopenharmony_ci				}
19762306a36Sopenharmony_ci			}
19862306a36Sopenharmony_ci		},
19962306a36Sopenharmony_ci		}},
20062306a36Sopenharmony_ci	} },
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	.i2c_algo = &dtv5100_i2c_algo,
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	.num_device_descs = 1,
20562306a36Sopenharmony_ci	.devices = {
20662306a36Sopenharmony_ci		{
20762306a36Sopenharmony_ci			.name = "AME DTV-5100 USB2.0 DVB-T",
20862306a36Sopenharmony_ci			.cold_ids = { NULL },
20962306a36Sopenharmony_ci			.warm_ids = { &dtv5100_table[AME_DTV5100], NULL },
21062306a36Sopenharmony_ci		},
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic struct usb_driver dtv5100_driver = {
21562306a36Sopenharmony_ci	.name		= "dvb_usb_dtv5100",
21662306a36Sopenharmony_ci	.probe		= dtv5100_probe,
21762306a36Sopenharmony_ci	.disconnect	= dvb_usb_device_exit,
21862306a36Sopenharmony_ci	.id_table	= dtv5100_table,
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cimodule_usb_driver(dtv5100_driver);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
22462306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
22562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
226