162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* DVB USB compliant Linux driver for the 362306a36Sopenharmony_ci * - TwinhanDTV Alpha/MagicBoxII USB2.0 DVB-T receiver 462306a36Sopenharmony_ci * - DigitalNow TinyUSB2 DVB-t receiver 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Thanks to Twinhan who kindly provided hardware and information. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include "vp7045.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* debug */ 1562306a36Sopenharmony_cistatic int dvb_usb_vp7045_debug; 1662306a36Sopenharmony_cimodule_param_named(debug,dvb_usb_vp7045_debug, int, 0644); 1762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args) 2262306a36Sopenharmony_ci#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args) 2362306a36Sopenharmony_ci#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciint vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci int ret = 0; 2862306a36Sopenharmony_ci u8 *buf = d->priv; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci buf[0] = cmd; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (outlen > 19) 3362306a36Sopenharmony_ci outlen = 19; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (inlen > 11) 3662306a36Sopenharmony_ci inlen = 11; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci ret = mutex_lock_interruptible(&d->usb_mutex); 3962306a36Sopenharmony_ci if (ret) 4062306a36Sopenharmony_ci return ret; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (out != NULL && outlen > 0) 4362306a36Sopenharmony_ci memcpy(&buf[1], out, outlen); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci deb_xfer("out buffer: "); 4662306a36Sopenharmony_ci debug_dump(buf, outlen+1, deb_xfer); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (usb_control_msg(d->udev, 5062306a36Sopenharmony_ci usb_sndctrlpipe(d->udev,0), 5162306a36Sopenharmony_ci TH_COMMAND_OUT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, 5262306a36Sopenharmony_ci buf, 20, 2000) != 20) { 5362306a36Sopenharmony_ci err("USB control message 'out' went wrong."); 5462306a36Sopenharmony_ci ret = -EIO; 5562306a36Sopenharmony_ci goto unlock; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci msleep(msec); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (usb_control_msg(d->udev, 6162306a36Sopenharmony_ci usb_rcvctrlpipe(d->udev,0), 6262306a36Sopenharmony_ci TH_COMMAND_IN, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, 6362306a36Sopenharmony_ci buf, 12, 2000) != 12) { 6462306a36Sopenharmony_ci err("USB control message 'in' went wrong."); 6562306a36Sopenharmony_ci ret = -EIO; 6662306a36Sopenharmony_ci goto unlock; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci deb_xfer("in buffer: "); 7062306a36Sopenharmony_ci debug_dump(buf, 12, deb_xfer); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (in != NULL && inlen > 0) 7362306a36Sopenharmony_ci memcpy(in, &buf[1], inlen); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciunlock: 7662306a36Sopenharmony_ci mutex_unlock(&d->usb_mutex); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return ret; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ciu8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u8 obuf[2] = { 0 },v; 8462306a36Sopenharmony_ci obuf[1] = reg; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci vp7045_usb_op(d,TUNER_REG_READ,obuf,2,&v,1,30); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return v; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci u8 v = onoff; 9462306a36Sopenharmony_ci return vp7045_usb_op(d,SET_TUNER_POWER,&v,1,NULL,0,150); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int vp7045_rc_query(struct dvb_usb_device *d) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci int ret; 10062306a36Sopenharmony_ci u8 key; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci ret = vp7045_usb_op(d, RC_VAL_READ, NULL, 0, &key, 1, 20); 10362306a36Sopenharmony_ci if (ret) 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci deb_rc("remote query key: %x\n", key); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (key != 0x44) { 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * The 8 bit address isn't available, but since the remote uses 11162306a36Sopenharmony_ci * address 0 we'll use that. nec repeats are ignored too, even 11262306a36Sopenharmony_ci * though the remote sends them. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci rc_keydown(d->rc_dev, RC_PROTO_NEC, RC_SCANCODE_NEC(0, key), 0); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int vp7045_read_eeprom(struct dvb_usb_device *d,u8 *buf, int len, int offset) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int i, ret; 12362306a36Sopenharmony_ci u8 v, br[2]; 12462306a36Sopenharmony_ci for (i=0; i < len; i++) { 12562306a36Sopenharmony_ci v = offset + i; 12662306a36Sopenharmony_ci ret = vp7045_usb_op(d, GET_EE_VALUE, &v, 1, br, 2, 5); 12762306a36Sopenharmony_ci if (ret) 12862306a36Sopenharmony_ci return ret; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci buf[i] = br[1]; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci deb_info("VP7045 EEPROM read (offs: %d, len: %d) : ", offset, i); 13362306a36Sopenharmony_ci debug_dump(buf, i, deb_info); 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int vp7045_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci return vp7045_read_eeprom(d,mac, 6, MAC_0_ADDR); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int vp7045_frontend_attach(struct dvb_usb_adapter *adap) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u8 buf[255] = { 0 }; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci vp7045_usb_op(adap->dev,VENDOR_STRING_READ,NULL,0,buf,20,0); 14762306a36Sopenharmony_ci buf[10] = '\0'; 14862306a36Sopenharmony_ci deb_info("firmware says: %s ",buf); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci vp7045_usb_op(adap->dev,PRODUCT_STRING_READ,NULL,0,buf,20,0); 15162306a36Sopenharmony_ci buf[10] = '\0'; 15262306a36Sopenharmony_ci deb_info("%s ",buf); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci vp7045_usb_op(adap->dev,FW_VERSION_READ,NULL,0,buf,20,0); 15562306a36Sopenharmony_ci buf[10] = '\0'; 15662306a36Sopenharmony_ci deb_info("v%s\n",buf); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* Dump the EEPROM */ 15962306a36Sopenharmony_ci/* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */ 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci adap->fe_adap[0].fe = vp7045_fe_attach(adap->dev); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct dvb_usb_device_properties vp7045_properties; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int vp7045_usb_probe(struct usb_interface *intf, 16962306a36Sopenharmony_ci const struct usb_device_id *id) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci return dvb_usb_device_init(intf, &vp7045_properties, 17262306a36Sopenharmony_ci THIS_MODULE, NULL, adapter_nr); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cienum { 17662306a36Sopenharmony_ci VISIONPLUS_VP7045_COLD, 17762306a36Sopenharmony_ci VISIONPLUS_VP7045_WARM, 17862306a36Sopenharmony_ci VISIONPLUS_TINYUSB2_COLD, 17962306a36Sopenharmony_ci VISIONPLUS_TINYUSB2_WARM, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic struct usb_device_id vp7045_usb_table[] = { 18362306a36Sopenharmony_ci DVB_USB_DEV(VISIONPLUS, VISIONPLUS_VP7045_COLD), 18462306a36Sopenharmony_ci DVB_USB_DEV(VISIONPLUS, VISIONPLUS_VP7045_WARM), 18562306a36Sopenharmony_ci DVB_USB_DEV(VISIONPLUS, VISIONPLUS_TINYUSB2_COLD), 18662306a36Sopenharmony_ci DVB_USB_DEV(VISIONPLUS, VISIONPLUS_TINYUSB2_WARM), 18762306a36Sopenharmony_ci { } 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, vp7045_usb_table); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic struct dvb_usb_device_properties vp7045_properties = { 19362306a36Sopenharmony_ci .usb_ctrl = CYPRESS_FX2, 19462306a36Sopenharmony_ci .firmware = "dvb-usb-vp7045-01.fw", 19562306a36Sopenharmony_ci .size_of_priv = 20, 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci .num_adapters = 1, 19862306a36Sopenharmony_ci .adapter = { 19962306a36Sopenharmony_ci { 20062306a36Sopenharmony_ci .num_frontends = 1, 20162306a36Sopenharmony_ci .fe = {{ 20262306a36Sopenharmony_ci .frontend_attach = vp7045_frontend_attach, 20362306a36Sopenharmony_ci /* parameter for the MPEG2-data transfer */ 20462306a36Sopenharmony_ci .stream = { 20562306a36Sopenharmony_ci .type = USB_BULK, 20662306a36Sopenharmony_ci .count = 7, 20762306a36Sopenharmony_ci .endpoint = 0x02, 20862306a36Sopenharmony_ci .u = { 20962306a36Sopenharmony_ci .bulk = { 21062306a36Sopenharmony_ci .buffersize = 4096, 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci }, 21462306a36Sopenharmony_ci }}, 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci }, 21762306a36Sopenharmony_ci .power_ctrl = vp7045_power_ctrl, 21862306a36Sopenharmony_ci .read_mac_address = vp7045_read_mac_addr, 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci .rc.core = { 22162306a36Sopenharmony_ci .rc_interval = 400, 22262306a36Sopenharmony_ci .rc_codes = RC_MAP_TWINHAN_VP1027_DVBS, 22362306a36Sopenharmony_ci .module_name = KBUILD_MODNAME, 22462306a36Sopenharmony_ci .rc_query = vp7045_rc_query, 22562306a36Sopenharmony_ci .allowed_protos = RC_PROTO_BIT_NEC, 22662306a36Sopenharmony_ci .scancode_mask = 0xff, 22762306a36Sopenharmony_ci }, 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci .num_device_descs = 2, 23062306a36Sopenharmony_ci .devices = { 23162306a36Sopenharmony_ci { .name = "Twinhan USB2.0 DVB-T receiver (TwinhanDTV Alpha/MagicBox II)", 23262306a36Sopenharmony_ci .cold_ids = { &vp7045_usb_table[VISIONPLUS_VP7045_COLD], NULL }, 23362306a36Sopenharmony_ci .warm_ids = { &vp7045_usb_table[VISIONPLUS_VP7045_WARM], NULL }, 23462306a36Sopenharmony_ci }, 23562306a36Sopenharmony_ci { .name = "DigitalNow TinyUSB 2 DVB-t Receiver", 23662306a36Sopenharmony_ci .cold_ids = { &vp7045_usb_table[VISIONPLUS_TINYUSB2_COLD], NULL }, 23762306a36Sopenharmony_ci .warm_ids = { &vp7045_usb_table[VISIONPLUS_TINYUSB2_WARM], NULL }, 23862306a36Sopenharmony_ci }, 23962306a36Sopenharmony_ci { NULL }, 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */ 24462306a36Sopenharmony_cistatic struct usb_driver vp7045_usb_driver = { 24562306a36Sopenharmony_ci .name = "dvb_usb_vp7045", 24662306a36Sopenharmony_ci .probe = vp7045_usb_probe, 24762306a36Sopenharmony_ci .disconnect = dvb_usb_device_exit, 24862306a36Sopenharmony_ci .id_table = vp7045_usb_table, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cimodule_usb_driver(vp7045_usb_driver); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ciMODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); 25462306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Twinhan MagicBox/Alpha and DNTV tinyUSB2 DVB-T USB2.0"); 25562306a36Sopenharmony_ciMODULE_VERSION("1.0"); 25662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 257