18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel CE6230 DVB USB driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "ce6230.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic int ce6230_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci int ret; 158c2ecf20Sopenharmony_ci unsigned int pipe; 168c2ecf20Sopenharmony_ci u8 request; 178c2ecf20Sopenharmony_ci u8 requesttype; 188c2ecf20Sopenharmony_ci u16 value; 198c2ecf20Sopenharmony_ci u16 index; 208c2ecf20Sopenharmony_ci u8 *buf; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci request = req->cmd; 238c2ecf20Sopenharmony_ci value = req->value; 248c2ecf20Sopenharmony_ci index = req->index; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci switch (req->cmd) { 278c2ecf20Sopenharmony_ci case I2C_READ: 288c2ecf20Sopenharmony_ci case DEMOD_READ: 298c2ecf20Sopenharmony_ci case REG_READ: 308c2ecf20Sopenharmony_ci requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci case I2C_WRITE: 338c2ecf20Sopenharmony_ci case DEMOD_WRITE: 348c2ecf20Sopenharmony_ci case REG_WRITE: 358c2ecf20Sopenharmony_ci requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); 368c2ecf20Sopenharmony_ci break; 378c2ecf20Sopenharmony_ci default: 388c2ecf20Sopenharmony_ci dev_err(&d->udev->dev, "%s: unknown command=%02x\n", 398c2ecf20Sopenharmony_ci KBUILD_MODNAME, req->cmd); 408c2ecf20Sopenharmony_ci ret = -EINVAL; 418c2ecf20Sopenharmony_ci goto error; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci buf = kmalloc(req->data_len, GFP_KERNEL); 458c2ecf20Sopenharmony_ci if (!buf) { 468c2ecf20Sopenharmony_ci ret = -ENOMEM; 478c2ecf20Sopenharmony_ci goto error; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) { 518c2ecf20Sopenharmony_ci /* write */ 528c2ecf20Sopenharmony_ci memcpy(buf, req->data, req->data_len); 538c2ecf20Sopenharmony_ci pipe = usb_sndctrlpipe(d->udev, 0); 548c2ecf20Sopenharmony_ci } else { 558c2ecf20Sopenharmony_ci /* read */ 568c2ecf20Sopenharmony_ci pipe = usb_rcvctrlpipe(d->udev, 0); 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci msleep(1); /* avoid I2C errors */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci ret = usb_control_msg(d->udev, pipe, request, requesttype, value, index, 628c2ecf20Sopenharmony_ci buf, req->data_len, CE6230_USB_TIMEOUT); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, value, index, 658c2ecf20Sopenharmony_ci buf, req->data_len); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (ret < 0) 688c2ecf20Sopenharmony_ci dev_err(&d->udev->dev, "%s: usb_control_msg() failed=%d\n", 698c2ecf20Sopenharmony_ci KBUILD_MODNAME, ret); 708c2ecf20Sopenharmony_ci else 718c2ecf20Sopenharmony_ci ret = 0; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* read request, copy returned data to return buf */ 748c2ecf20Sopenharmony_ci if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) 758c2ecf20Sopenharmony_ci memcpy(req->data, buf, req->data_len); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci kfree(buf); 788c2ecf20Sopenharmony_cierror: 798c2ecf20Sopenharmony_ci return ret; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* I2C */ 838c2ecf20Sopenharmony_cistatic struct zl10353_config ce6230_zl10353_config; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int ce6230_i2c_master_xfer(struct i2c_adapter *adap, 868c2ecf20Sopenharmony_ci struct i2c_msg msg[], int num) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct dvb_usb_device *d = i2c_get_adapdata(adap); 898c2ecf20Sopenharmony_ci int ret = 0, i = 0; 908c2ecf20Sopenharmony_ci struct usb_req req; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (num > 2) 938c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci memset(&req, 0, sizeof(req)); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&d->i2c_mutex) < 0) 988c2ecf20Sopenharmony_ci return -EAGAIN; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci while (i < num) { 1018c2ecf20Sopenharmony_ci if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { 1028c2ecf20Sopenharmony_ci if (msg[i].addr == 1038c2ecf20Sopenharmony_ci ce6230_zl10353_config.demod_address) { 1048c2ecf20Sopenharmony_ci if (msg[i].len < 1) { 1058c2ecf20Sopenharmony_ci i = -EOPNOTSUPP; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci req.cmd = DEMOD_READ; 1098c2ecf20Sopenharmony_ci req.value = msg[i].addr >> 1; 1108c2ecf20Sopenharmony_ci req.index = msg[i].buf[0]; 1118c2ecf20Sopenharmony_ci req.data_len = msg[i+1].len; 1128c2ecf20Sopenharmony_ci req.data = &msg[i+1].buf[0]; 1138c2ecf20Sopenharmony_ci ret = ce6230_ctrl_msg(d, &req); 1148c2ecf20Sopenharmony_ci } else { 1158c2ecf20Sopenharmony_ci dev_err(&d->udev->dev, "%s: I2C read not " \ 1168c2ecf20Sopenharmony_ci "implemented\n", 1178c2ecf20Sopenharmony_ci KBUILD_MODNAME); 1188c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci i += 2; 1218c2ecf20Sopenharmony_ci } else { 1228c2ecf20Sopenharmony_ci if (msg[i].addr == 1238c2ecf20Sopenharmony_ci ce6230_zl10353_config.demod_address) { 1248c2ecf20Sopenharmony_ci if (msg[i].len < 1) { 1258c2ecf20Sopenharmony_ci i = -EOPNOTSUPP; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci req.cmd = DEMOD_WRITE; 1298c2ecf20Sopenharmony_ci req.value = msg[i].addr >> 1; 1308c2ecf20Sopenharmony_ci req.index = msg[i].buf[0]; 1318c2ecf20Sopenharmony_ci req.data_len = msg[i].len-1; 1328c2ecf20Sopenharmony_ci req.data = &msg[i].buf[1]; 1338c2ecf20Sopenharmony_ci ret = ce6230_ctrl_msg(d, &req); 1348c2ecf20Sopenharmony_ci } else { 1358c2ecf20Sopenharmony_ci req.cmd = I2C_WRITE; 1368c2ecf20Sopenharmony_ci req.value = 0x2000 + (msg[i].addr >> 1); 1378c2ecf20Sopenharmony_ci req.index = 0x0000; 1388c2ecf20Sopenharmony_ci req.data_len = msg[i].len; 1398c2ecf20Sopenharmony_ci req.data = &msg[i].buf[0]; 1408c2ecf20Sopenharmony_ci ret = ce6230_ctrl_msg(d, &req); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci i += 1; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci if (ret) 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci mutex_unlock(&d->i2c_mutex); 1498c2ecf20Sopenharmony_ci return ret ? ret : i; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic u32 ce6230_i2c_functionality(struct i2c_adapter *adapter) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci return I2C_FUNC_I2C; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic struct i2c_algorithm ce6230_i2c_algorithm = { 1588c2ecf20Sopenharmony_ci .master_xfer = ce6230_i2c_master_xfer, 1598c2ecf20Sopenharmony_ci .functionality = ce6230_i2c_functionality, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* Callbacks for DVB USB */ 1638c2ecf20Sopenharmony_cistatic struct zl10353_config ce6230_zl10353_config = { 1648c2ecf20Sopenharmony_ci .demod_address = 0x1e, 1658c2ecf20Sopenharmony_ci .adc_clock = 450000, 1668c2ecf20Sopenharmony_ci .if2 = 45700, 1678c2ecf20Sopenharmony_ci .no_tuner = 1, 1688c2ecf20Sopenharmony_ci .parallel_ts = 1, 1698c2ecf20Sopenharmony_ci .clock_ctl_1 = 0x34, 1708c2ecf20Sopenharmony_ci .pll_0 = 0x0e, 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct dvb_usb_device *d = adap_to_d(adap); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci dev_dbg(&d->udev->dev, "%s:\n", __func__); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci adap->fe[0] = dvb_attach(zl10353_attach, &ce6230_zl10353_config, 1808c2ecf20Sopenharmony_ci &d->i2c_adap); 1818c2ecf20Sopenharmony_ci if (adap->fe[0] == NULL) 1828c2ecf20Sopenharmony_ci return -ENODEV; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic struct mxl5005s_config ce6230_mxl5003s_config = { 1888c2ecf20Sopenharmony_ci .i2c_address = 0xc6, 1898c2ecf20Sopenharmony_ci .if_freq = IF_FREQ_4570000HZ, 1908c2ecf20Sopenharmony_ci .xtal_freq = CRYSTAL_FREQ_16000000HZ, 1918c2ecf20Sopenharmony_ci .agc_mode = MXL_SINGLE_AGC, 1928c2ecf20Sopenharmony_ci .tracking_filter = MXL_TF_DEFAULT, 1938c2ecf20Sopenharmony_ci .rssi_enable = MXL_RSSI_ENABLE, 1948c2ecf20Sopenharmony_ci .cap_select = MXL_CAP_SEL_ENABLE, 1958c2ecf20Sopenharmony_ci .div_out = MXL_DIV_OUT_4, 1968c2ecf20Sopenharmony_ci .clock_out = MXL_CLOCK_OUT_DISABLE, 1978c2ecf20Sopenharmony_ci .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, 1988c2ecf20Sopenharmony_ci .top = MXL5005S_TOP_25P2, 1998c2ecf20Sopenharmony_ci .mod_mode = MXL_DIGITAL_MODE, 2008c2ecf20Sopenharmony_ci .if_mode = MXL_ZERO_IF, 2018c2ecf20Sopenharmony_ci .AgcMasterByte = 0x00, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct dvb_usb_device *d = adap_to_d(adap); 2078c2ecf20Sopenharmony_ci int ret; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci dev_dbg(&d->udev->dev, "%s:\n", __func__); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ret = dvb_attach(mxl5005s_attach, adap->fe[0], &d->i2c_adap, 2128c2ecf20Sopenharmony_ci &ce6230_mxl5003s_config) == NULL ? -ENODEV : 0; 2138c2ecf20Sopenharmony_ci return ret; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int ce6230_power_ctrl(struct dvb_usb_device *d, int onoff) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci int ret; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* InterfaceNumber 1 / AlternateSetting 0 idle 2238c2ecf20Sopenharmony_ci InterfaceNumber 1 / AlternateSetting 1 streaming */ 2248c2ecf20Sopenharmony_ci ret = usb_set_interface(d->udev, 1, onoff); 2258c2ecf20Sopenharmony_ci if (ret) 2268c2ecf20Sopenharmony_ci dev_err(&d->udev->dev, "%s: usb_set_interface() failed=%d\n", 2278c2ecf20Sopenharmony_ci KBUILD_MODNAME, ret); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return ret; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* DVB USB Driver stuff */ 2338c2ecf20Sopenharmony_cistatic struct dvb_usb_device_properties ce6230_props = { 2348c2ecf20Sopenharmony_ci .driver_name = KBUILD_MODNAME, 2358c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2368c2ecf20Sopenharmony_ci .adapter_nr = adapter_nr, 2378c2ecf20Sopenharmony_ci .bInterfaceNumber = 1, 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci .i2c_algo = &ce6230_i2c_algorithm, 2408c2ecf20Sopenharmony_ci .power_ctrl = ce6230_power_ctrl, 2418c2ecf20Sopenharmony_ci .frontend_attach = ce6230_zl10353_frontend_attach, 2428c2ecf20Sopenharmony_ci .tuner_attach = ce6230_mxl5003s_tuner_attach, 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci .num_adapters = 1, 2458c2ecf20Sopenharmony_ci .adapter = { 2468c2ecf20Sopenharmony_ci { 2478c2ecf20Sopenharmony_ci .stream = { 2488c2ecf20Sopenharmony_ci .type = USB_BULK, 2498c2ecf20Sopenharmony_ci .count = 6, 2508c2ecf20Sopenharmony_ci .endpoint = 0x82, 2518c2ecf20Sopenharmony_ci .u = { 2528c2ecf20Sopenharmony_ci .bulk = { 2538c2ecf20Sopenharmony_ci .buffersize = (16 * 512), 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci }, 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci }, 2598c2ecf20Sopenharmony_ci}; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic const struct usb_device_id ce6230_id_table[] = { 2628c2ecf20Sopenharmony_ci { DVB_USB_DEVICE(USB_VID_INTEL, USB_PID_INTEL_CE9500, 2638c2ecf20Sopenharmony_ci &ce6230_props, "Intel CE9500 reference design", NULL) }, 2648c2ecf20Sopenharmony_ci { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A310, 2658c2ecf20Sopenharmony_ci &ce6230_props, "AVerMedia A310 USB 2.0 DVB-T tuner", NULL) }, 2668c2ecf20Sopenharmony_ci { } 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ce6230_id_table); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic struct usb_driver ce6230_usb_driver = { 2718c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 2728c2ecf20Sopenharmony_ci .id_table = ce6230_id_table, 2738c2ecf20Sopenharmony_ci .probe = dvb_usbv2_probe, 2748c2ecf20Sopenharmony_ci .disconnect = dvb_usbv2_disconnect, 2758c2ecf20Sopenharmony_ci .suspend = dvb_usbv2_suspend, 2768c2ecf20Sopenharmony_ci .resume = dvb_usbv2_resume, 2778c2ecf20Sopenharmony_ci .reset_resume = dvb_usbv2_reset_resume, 2788c2ecf20Sopenharmony_ci .no_dynamic_id = 1, 2798c2ecf20Sopenharmony_ci .soft_unbind = 1, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cimodule_usb_driver(ce6230_usb_driver); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 2858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel CE6230 driver"); 2868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 287