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