18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Antoine Jacquet <royale@zerezo.com> 68c2ecf20Sopenharmony_ci * http://royale.zerezo.com/dtv5100/ 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Inspired by gl861.c and au6610.c drivers 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "dtv5100.h" 128c2ecf20Sopenharmony_ci#include "zl10353.h" 138c2ecf20Sopenharmony_ci#include "qt1010.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* debug */ 168c2ecf20Sopenharmony_cistatic int dvb_usb_dtv5100_debug; 178c2ecf20Sopenharmony_cimodule_param_named(debug, dvb_usb_dtv5100_debug, int, 0644); 188c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); 198c2ecf20Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct dtv5100_state { 228c2ecf20Sopenharmony_ci unsigned char data[80]; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr, 268c2ecf20Sopenharmony_ci u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct dtv5100_state *st = d->priv; 298c2ecf20Sopenharmony_ci unsigned int pipe; 308c2ecf20Sopenharmony_ci u8 request; 318c2ecf20Sopenharmony_ci u8 type; 328c2ecf20Sopenharmony_ci u16 value; 338c2ecf20Sopenharmony_ci u16 index; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci switch (wlen) { 368c2ecf20Sopenharmony_ci case 1: 378c2ecf20Sopenharmony_ci /* write { reg }, read { value } */ 388c2ecf20Sopenharmony_ci pipe = usb_rcvctrlpipe(d->udev, 0); 398c2ecf20Sopenharmony_ci request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ : 408c2ecf20Sopenharmony_ci DTV5100_TUNER_READ); 418c2ecf20Sopenharmony_ci type = USB_TYPE_VENDOR | USB_DIR_IN; 428c2ecf20Sopenharmony_ci value = 0; 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci case 2: 458c2ecf20Sopenharmony_ci /* write { reg, value } */ 468c2ecf20Sopenharmony_ci pipe = usb_sndctrlpipe(d->udev, 0); 478c2ecf20Sopenharmony_ci request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE : 488c2ecf20Sopenharmony_ci DTV5100_TUNER_WRITE); 498c2ecf20Sopenharmony_ci type = USB_TYPE_VENDOR | USB_DIR_OUT; 508c2ecf20Sopenharmony_ci value = wbuf[1]; 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci default: 538c2ecf20Sopenharmony_ci warn("wlen = %x, aborting.", wlen); 548c2ecf20Sopenharmony_ci return -EINVAL; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci index = (addr << 8) + wbuf[0]; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci memcpy(st->data, rbuf, rlen); 598c2ecf20Sopenharmony_ci msleep(1); /* avoid I2C errors */ 608c2ecf20Sopenharmony_ci return usb_control_msg(d->udev, pipe, request, 618c2ecf20Sopenharmony_ci type, value, index, st->data, rlen, 628c2ecf20Sopenharmony_ci DTV5100_USB_TIMEOUT); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* I2C */ 668c2ecf20Sopenharmony_cistatic int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], 678c2ecf20Sopenharmony_ci int num) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct dvb_usb_device *d = i2c_get_adapdata(adap); 708c2ecf20Sopenharmony_ci int i; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (num > 2) 738c2ecf20Sopenharmony_ci return -EINVAL; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&d->i2c_mutex) < 0) 768c2ecf20Sopenharmony_ci return -EAGAIN; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 798c2ecf20Sopenharmony_ci /* write/read request */ 808c2ecf20Sopenharmony_ci if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { 818c2ecf20Sopenharmony_ci if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, 828c2ecf20Sopenharmony_ci msg[i].len, msg[i+1].buf, 838c2ecf20Sopenharmony_ci msg[i+1].len) < 0) 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci i++; 868c2ecf20Sopenharmony_ci } else if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, 878c2ecf20Sopenharmony_ci msg[i].len, NULL, 0) < 0) 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci mutex_unlock(&d->i2c_mutex); 928c2ecf20Sopenharmony_ci return i; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic u32 dtv5100_i2c_func(struct i2c_adapter *adapter) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return I2C_FUNC_I2C; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic struct i2c_algorithm dtv5100_i2c_algo = { 1018c2ecf20Sopenharmony_ci .master_xfer = dtv5100_i2c_xfer, 1028c2ecf20Sopenharmony_ci .functionality = dtv5100_i2c_func, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* Callbacks for DVB USB */ 1068c2ecf20Sopenharmony_cistatic struct zl10353_config dtv5100_zl10353_config = { 1078c2ecf20Sopenharmony_ci .demod_address = DTV5100_DEMOD_ADDR, 1088c2ecf20Sopenharmony_ci .no_tuner = 1, 1098c2ecf20Sopenharmony_ci .parallel_ts = 1, 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int dtv5100_frontend_attach(struct dvb_usb_adapter *adap) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config, 1158c2ecf20Sopenharmony_ci &adap->dev->i2c_adap); 1168c2ecf20Sopenharmony_ci if (adap->fe_adap[0].fe == NULL) 1178c2ecf20Sopenharmony_ci return -EIO; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* disable i2c gate, or it won't work... is this safe? */ 1208c2ecf20Sopenharmony_ci adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic struct qt1010_config dtv5100_qt1010_config = { 1268c2ecf20Sopenharmony_ci .i2c_address = DTV5100_TUNER_ADDR 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int dtv5100_tuner_attach(struct dvb_usb_adapter *adap) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci return dvb_attach(qt1010_attach, 1328c2ecf20Sopenharmony_ci adap->fe_adap[0].fe, &adap->dev->i2c_adap, 1338c2ecf20Sopenharmony_ci &dtv5100_qt1010_config) == NULL ? -ENODEV : 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* DVB USB Driver stuff */ 1378c2ecf20Sopenharmony_cistatic struct dvb_usb_device_properties dtv5100_properties; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int dtv5100_probe(struct usb_interface *intf, 1408c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int i, ret; 1438c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* initialize non qt1010/zl10353 part? */ 1468c2ecf20Sopenharmony_ci for (i = 0; dtv5100_init[i].request; i++) { 1478c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 1488c2ecf20Sopenharmony_ci dtv5100_init[i].request, 1498c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_OUT, 1508c2ecf20Sopenharmony_ci dtv5100_init[i].value, 1518c2ecf20Sopenharmony_ci dtv5100_init[i].index, NULL, 0, 1528c2ecf20Sopenharmony_ci DTV5100_USB_TIMEOUT); 1538c2ecf20Sopenharmony_ci if (ret) 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci ret = dvb_usb_device_init(intf, &dtv5100_properties, 1588c2ecf20Sopenharmony_ci THIS_MODULE, NULL, adapter_nr); 1598c2ecf20Sopenharmony_ci if (ret) 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic struct usb_device_id dtv5100_table[] = { 1668c2ecf20Sopenharmony_ci { USB_DEVICE(0x06be, 0xa232) }, 1678c2ecf20Sopenharmony_ci { } /* Terminating entry */ 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, dtv5100_table); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic struct dvb_usb_device_properties dtv5100_properties = { 1728c2ecf20Sopenharmony_ci .caps = DVB_USB_IS_AN_I2C_ADAPTER, 1738c2ecf20Sopenharmony_ci .usb_ctrl = DEVICE_SPECIFIC, 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci .size_of_priv = sizeof(struct dtv5100_state), 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci .num_adapters = 1, 1788c2ecf20Sopenharmony_ci .adapter = {{ 1798c2ecf20Sopenharmony_ci .num_frontends = 1, 1808c2ecf20Sopenharmony_ci .fe = {{ 1818c2ecf20Sopenharmony_ci .frontend_attach = dtv5100_frontend_attach, 1828c2ecf20Sopenharmony_ci .tuner_attach = dtv5100_tuner_attach, 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci .stream = { 1858c2ecf20Sopenharmony_ci .type = USB_BULK, 1868c2ecf20Sopenharmony_ci .count = 8, 1878c2ecf20Sopenharmony_ci .endpoint = 0x82, 1888c2ecf20Sopenharmony_ci .u = { 1898c2ecf20Sopenharmony_ci .bulk = { 1908c2ecf20Sopenharmony_ci .buffersize = 4096, 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci }, 1948c2ecf20Sopenharmony_ci }}, 1958c2ecf20Sopenharmony_ci } }, 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci .i2c_algo = &dtv5100_i2c_algo, 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci .num_device_descs = 1, 2008c2ecf20Sopenharmony_ci .devices = { 2018c2ecf20Sopenharmony_ci { 2028c2ecf20Sopenharmony_ci .name = "AME DTV-5100 USB2.0 DVB-T", 2038c2ecf20Sopenharmony_ci .cold_ids = { NULL }, 2048c2ecf20Sopenharmony_ci .warm_ids = { &dtv5100_table[0], NULL }, 2058c2ecf20Sopenharmony_ci }, 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct usb_driver dtv5100_driver = { 2108c2ecf20Sopenharmony_ci .name = "dvb_usb_dtv5100", 2118c2ecf20Sopenharmony_ci .probe = dtv5100_probe, 2128c2ecf20Sopenharmony_ci .disconnect = dvb_usb_device_exit, 2138c2ecf20Sopenharmony_ci .id_table = dtv5100_table, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cimodule_usb_driver(dtv5100_driver); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 2198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 2208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 221