18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* DVB USB compliant Linux driver for the 38c2ecf20Sopenharmony_ci * - TwinhanDTV Alpha/MagicBoxII USB2.0 DVB-T receiver 48c2ecf20Sopenharmony_ci * - DigitalNow TinyUSB2 DVB-t receiver 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Thanks to Twinhan who kindly provided hardware and information. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#include "vp7045.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* debug */ 158c2ecf20Sopenharmony_cistatic int dvb_usb_vp7045_debug; 168c2ecf20Sopenharmony_cimodule_param_named(debug,dvb_usb_vp7045_debug, int, 0644); 178c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args) 228c2ecf20Sopenharmony_ci#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args) 238c2ecf20Sopenharmony_ci#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciint vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci int ret = 0; 288c2ecf20Sopenharmony_ci u8 *buf = d->priv; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci buf[0] = cmd; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (outlen > 19) 338c2ecf20Sopenharmony_ci outlen = 19; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (inlen > 11) 368c2ecf20Sopenharmony_ci inlen = 11; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&d->usb_mutex); 398c2ecf20Sopenharmony_ci if (ret) 408c2ecf20Sopenharmony_ci return ret; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (out != NULL && outlen > 0) 438c2ecf20Sopenharmony_ci memcpy(&buf[1], out, outlen); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci deb_xfer("out buffer: "); 468c2ecf20Sopenharmony_ci debug_dump(buf, outlen+1, deb_xfer); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (usb_control_msg(d->udev, 508c2ecf20Sopenharmony_ci usb_sndctrlpipe(d->udev,0), 518c2ecf20Sopenharmony_ci TH_COMMAND_OUT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, 528c2ecf20Sopenharmony_ci buf, 20, 2000) != 20) { 538c2ecf20Sopenharmony_ci err("USB control message 'out' went wrong."); 548c2ecf20Sopenharmony_ci ret = -EIO; 558c2ecf20Sopenharmony_ci goto unlock; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci msleep(msec); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (usb_control_msg(d->udev, 618c2ecf20Sopenharmony_ci usb_rcvctrlpipe(d->udev,0), 628c2ecf20Sopenharmony_ci TH_COMMAND_IN, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, 638c2ecf20Sopenharmony_ci buf, 12, 2000) != 12) { 648c2ecf20Sopenharmony_ci err("USB control message 'in' went wrong."); 658c2ecf20Sopenharmony_ci ret = -EIO; 668c2ecf20Sopenharmony_ci goto unlock; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci deb_xfer("in buffer: "); 708c2ecf20Sopenharmony_ci debug_dump(buf, 12, deb_xfer); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (in != NULL && inlen > 0) 738c2ecf20Sopenharmony_ci memcpy(in, &buf[1], inlen); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ciunlock: 768c2ecf20Sopenharmony_ci mutex_unlock(&d->usb_mutex); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ciu8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u8 obuf[2] = { 0 },v; 848c2ecf20Sopenharmony_ci obuf[1] = reg; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci vp7045_usb_op(d,TUNER_REG_READ,obuf,2,&v,1,30); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return v; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci u8 v = onoff; 948c2ecf20Sopenharmony_ci return vp7045_usb_op(d,SET_TUNER_POWER,&v,1,NULL,0,150); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int vp7045_rc_query(struct dvb_usb_device *d) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int ret; 1008c2ecf20Sopenharmony_ci u8 key; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ret = vp7045_usb_op(d, RC_VAL_READ, NULL, 0, &key, 1, 20); 1038c2ecf20Sopenharmony_ci if (ret) 1048c2ecf20Sopenharmony_ci return ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci deb_rc("remote query key: %x\n", key); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (key != 0x44) { 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * The 8 bit address isn't available, but since the remote uses 1118c2ecf20Sopenharmony_ci * address 0 we'll use that. nec repeats are ignored too, even 1128c2ecf20Sopenharmony_ci * though the remote sends them. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci rc_keydown(d->rc_dev, RC_PROTO_NEC, RC_SCANCODE_NEC(0, key), 0); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int vp7045_read_eeprom(struct dvb_usb_device *d,u8 *buf, int len, int offset) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int i, ret; 1238c2ecf20Sopenharmony_ci u8 v, br[2]; 1248c2ecf20Sopenharmony_ci for (i=0; i < len; i++) { 1258c2ecf20Sopenharmony_ci v = offset + i; 1268c2ecf20Sopenharmony_ci ret = vp7045_usb_op(d, GET_EE_VALUE, &v, 1, br, 2, 5); 1278c2ecf20Sopenharmony_ci if (ret) 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci buf[i] = br[1]; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci deb_info("VP7045 EEPROM read (offs: %d, len: %d) : ", offset, i); 1338c2ecf20Sopenharmony_ci debug_dump(buf, i, deb_info); 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int vp7045_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci return vp7045_read_eeprom(d,mac, 6, MAC_0_ADDR); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int vp7045_frontend_attach(struct dvb_usb_adapter *adap) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci u8 buf[255] = { 0 }; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci vp7045_usb_op(adap->dev,VENDOR_STRING_READ,NULL,0,buf,20,0); 1478c2ecf20Sopenharmony_ci buf[10] = '\0'; 1488c2ecf20Sopenharmony_ci deb_info("firmware says: %s ",buf); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci vp7045_usb_op(adap->dev,PRODUCT_STRING_READ,NULL,0,buf,20,0); 1518c2ecf20Sopenharmony_ci buf[10] = '\0'; 1528c2ecf20Sopenharmony_ci deb_info("%s ",buf); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci vp7045_usb_op(adap->dev,FW_VERSION_READ,NULL,0,buf,20,0); 1558c2ecf20Sopenharmony_ci buf[10] = '\0'; 1568c2ecf20Sopenharmony_ci deb_info("v%s\n",buf); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* Dump the EEPROM */ 1598c2ecf20Sopenharmony_ci/* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */ 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci adap->fe_adap[0].fe = vp7045_fe_attach(adap->dev); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic struct dvb_usb_device_properties vp7045_properties; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int vp7045_usb_probe(struct usb_interface *intf, 1698c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci return dvb_usb_device_init(intf, &vp7045_properties, 1728c2ecf20Sopenharmony_ci THIS_MODULE, NULL, adapter_nr); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic struct usb_device_id vp7045_usb_table [] = { 1768c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_COLD) }, 1778c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_WARM) }, 1788c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_COLD) }, 1798c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_WARM) }, 1808c2ecf20Sopenharmony_ci { 0 }, 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, vp7045_usb_table); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct dvb_usb_device_properties vp7045_properties = { 1858c2ecf20Sopenharmony_ci .usb_ctrl = CYPRESS_FX2, 1868c2ecf20Sopenharmony_ci .firmware = "dvb-usb-vp7045-01.fw", 1878c2ecf20Sopenharmony_ci .size_of_priv = 20, 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci .num_adapters = 1, 1908c2ecf20Sopenharmony_ci .adapter = { 1918c2ecf20Sopenharmony_ci { 1928c2ecf20Sopenharmony_ci .num_frontends = 1, 1938c2ecf20Sopenharmony_ci .fe = {{ 1948c2ecf20Sopenharmony_ci .frontend_attach = vp7045_frontend_attach, 1958c2ecf20Sopenharmony_ci /* parameter for the MPEG2-data transfer */ 1968c2ecf20Sopenharmony_ci .stream = { 1978c2ecf20Sopenharmony_ci .type = USB_BULK, 1988c2ecf20Sopenharmony_ci .count = 7, 1998c2ecf20Sopenharmony_ci .endpoint = 0x02, 2008c2ecf20Sopenharmony_ci .u = { 2018c2ecf20Sopenharmony_ci .bulk = { 2028c2ecf20Sopenharmony_ci .buffersize = 4096, 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci }, 2068c2ecf20Sopenharmony_ci }}, 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci }, 2098c2ecf20Sopenharmony_ci .power_ctrl = vp7045_power_ctrl, 2108c2ecf20Sopenharmony_ci .read_mac_address = vp7045_read_mac_addr, 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci .rc.core = { 2138c2ecf20Sopenharmony_ci .rc_interval = 400, 2148c2ecf20Sopenharmony_ci .rc_codes = RC_MAP_TWINHAN_VP1027_DVBS, 2158c2ecf20Sopenharmony_ci .module_name = KBUILD_MODNAME, 2168c2ecf20Sopenharmony_ci .rc_query = vp7045_rc_query, 2178c2ecf20Sopenharmony_ci .allowed_protos = RC_PROTO_BIT_NEC, 2188c2ecf20Sopenharmony_ci .scancode_mask = 0xff, 2198c2ecf20Sopenharmony_ci }, 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci .num_device_descs = 2, 2228c2ecf20Sopenharmony_ci .devices = { 2238c2ecf20Sopenharmony_ci { .name = "Twinhan USB2.0 DVB-T receiver (TwinhanDTV Alpha/MagicBox II)", 2248c2ecf20Sopenharmony_ci .cold_ids = { &vp7045_usb_table[0], NULL }, 2258c2ecf20Sopenharmony_ci .warm_ids = { &vp7045_usb_table[1], NULL }, 2268c2ecf20Sopenharmony_ci }, 2278c2ecf20Sopenharmony_ci { .name = "DigitalNow TinyUSB 2 DVB-t Receiver", 2288c2ecf20Sopenharmony_ci .cold_ids = { &vp7045_usb_table[2], NULL }, 2298c2ecf20Sopenharmony_ci .warm_ids = { &vp7045_usb_table[3], NULL }, 2308c2ecf20Sopenharmony_ci }, 2318c2ecf20Sopenharmony_ci { NULL }, 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */ 2368c2ecf20Sopenharmony_cistatic struct usb_driver vp7045_usb_driver = { 2378c2ecf20Sopenharmony_ci .name = "dvb_usb_vp7045", 2388c2ecf20Sopenharmony_ci .probe = vp7045_usb_probe, 2398c2ecf20Sopenharmony_ci .disconnect = dvb_usb_device_exit, 2408c2ecf20Sopenharmony_ci .id_table = vp7045_usb_table, 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cimodule_usb_driver(vp7045_usb_driver); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); 2468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Twinhan MagicBox/Alpha and DNTV tinyUSB2 DVB-T USB2.0"); 2478c2ecf20Sopenharmony_ciMODULE_VERSION("1.0"); 2488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 249