18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card 38c2ecf20Sopenharmony_ci* 48c2ecf20Sopenharmony_ci* Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org) 58c2ecf20Sopenharmony_ci* Copyright (C) 2006 Marco Gittler (g.marco@freenet.de) 68c2ecf20Sopenharmony_ci* 78c2ecf20Sopenharmony_ci* see Documentation/driver-api/media/drivers/dvb-usb.rst for more information 88c2ecf20Sopenharmony_ci*/ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define DVB_USB_LOG_PREFIX "opera" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "dvb-usb.h" 138c2ecf20Sopenharmony_ci#include "stv0299.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define OPERA_READ_MSG 0 168c2ecf20Sopenharmony_ci#define OPERA_WRITE_MSG 1 178c2ecf20Sopenharmony_ci#define OPERA_I2C_TUNER 0xd1 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define READ_FX2_REG_REQ 0xba 208c2ecf20Sopenharmony_ci#define READ_MAC_ADDR 0x08 218c2ecf20Sopenharmony_ci#define OPERA_WRITE_FX2 0xbb 228c2ecf20Sopenharmony_ci#define OPERA_TUNER_REQ 0xb1 238c2ecf20Sopenharmony_ci#define REG_1F_SYMBOLRATE_BYTE0 0x1f 248c2ecf20Sopenharmony_ci#define REG_20_SYMBOLRATE_BYTE1 0x20 258c2ecf20Sopenharmony_ci#define REG_21_SYMBOLRATE_BYTE2 0x21 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define ADDR_B600_VOLTAGE_13V (0x02) 288c2ecf20Sopenharmony_ci#define ADDR_B601_VOLTAGE_18V (0x03) 298c2ecf20Sopenharmony_ci#define ADDR_B1A6_STREAM_CTRL (0x04) 308c2ecf20Sopenharmony_ci#define ADDR_B880_READ_REMOTE (0x05) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct opera1_state { 338c2ecf20Sopenharmony_ci u32 last_key_pressed; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_cistruct rc_map_opera_table { 368c2ecf20Sopenharmony_ci u32 keycode; 378c2ecf20Sopenharmony_ci u32 event; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int dvb_usb_opera1_debug; 418c2ecf20Sopenharmony_cimodule_param_named(debug, dvb_usb_opera1_debug, int, 0644); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, 438c2ecf20Sopenharmony_ci "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))." 448c2ecf20Sopenharmony_ci DVB_USB_DEBUG_STATUS); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value, 508c2ecf20Sopenharmony_ci u8 * data, u16 len, int flags) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci int ret; 538c2ecf20Sopenharmony_ci u8 tmp; 548c2ecf20Sopenharmony_ci u8 *buf; 558c2ecf20Sopenharmony_ci unsigned int pipe = (flags == OPERA_READ_MSG) ? 568c2ecf20Sopenharmony_ci usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0); 578c2ecf20Sopenharmony_ci u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci buf = kmalloc(len, GFP_KERNEL); 608c2ecf20Sopenharmony_ci if (!buf) 618c2ecf20Sopenharmony_ci return -ENOMEM; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (flags == OPERA_WRITE_MSG) 648c2ecf20Sopenharmony_ci memcpy(buf, data, len); 658c2ecf20Sopenharmony_ci ret = usb_control_msg(dev, pipe, request, 668c2ecf20Sopenharmony_ci request_type | USB_TYPE_VENDOR, value, 0x0, 678c2ecf20Sopenharmony_ci buf, len, 2000); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (request == OPERA_TUNER_REQ) { 708c2ecf20Sopenharmony_ci tmp = buf[0]; 718c2ecf20Sopenharmony_ci if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 728c2ecf20Sopenharmony_ci OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR, 738c2ecf20Sopenharmony_ci 0x01, 0x0, buf, 1, 2000) < 1 || buf[0] != 0x08) { 748c2ecf20Sopenharmony_ci ret = 0; 758c2ecf20Sopenharmony_ci goto out; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci buf[0] = tmp; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci if (flags == OPERA_READ_MSG) 808c2ecf20Sopenharmony_ci memcpy(data, buf, len); 818c2ecf20Sopenharmony_ciout: 828c2ecf20Sopenharmony_ci kfree(buf); 838c2ecf20Sopenharmony_ci return ret; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* I2C */ 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr, 898c2ecf20Sopenharmony_ci u8 * buf, u16 len) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int ret = 0; 928c2ecf20Sopenharmony_ci u8 request; 938c2ecf20Sopenharmony_ci u16 value; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (!dev) { 968c2ecf20Sopenharmony_ci info("no usb_device"); 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&dev->usb_mutex) < 0) 1008c2ecf20Sopenharmony_ci return -EAGAIN; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci switch (addr>>1){ 1038c2ecf20Sopenharmony_ci case ADDR_B600_VOLTAGE_13V: 1048c2ecf20Sopenharmony_ci request=0xb6; 1058c2ecf20Sopenharmony_ci value=0x00; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci case ADDR_B601_VOLTAGE_18V: 1088c2ecf20Sopenharmony_ci request=0xb6; 1098c2ecf20Sopenharmony_ci value=0x01; 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case ADDR_B1A6_STREAM_CTRL: 1128c2ecf20Sopenharmony_ci request=0xb1; 1138c2ecf20Sopenharmony_ci value=0xa6; 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case ADDR_B880_READ_REMOTE: 1168c2ecf20Sopenharmony_ci request=0xb8; 1178c2ecf20Sopenharmony_ci value=0x80; 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci default: 1208c2ecf20Sopenharmony_ci request=0xb1; 1218c2ecf20Sopenharmony_ci value=addr; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci ret = opera1_xilinx_rw(dev->udev, request, 1248c2ecf20Sopenharmony_ci value, buf, len, 1258c2ecf20Sopenharmony_ci addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci mutex_unlock(&dev->usb_mutex); 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], 1328c2ecf20Sopenharmony_ci int num) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct dvb_usb_device *d = i2c_get_adapdata(adap); 1358c2ecf20Sopenharmony_ci int i = 0, tmp = 0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (!d) 1388c2ecf20Sopenharmony_ci return -ENODEV; 1398c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&d->i2c_mutex) < 0) 1408c2ecf20Sopenharmony_ci return -EAGAIN; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 1438c2ecf20Sopenharmony_ci if ((tmp = opera1_usb_i2c_msgxfer(d, 1448c2ecf20Sopenharmony_ci (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), 1458c2ecf20Sopenharmony_ci msg[i].buf, 1468c2ecf20Sopenharmony_ci msg[i].len 1478c2ecf20Sopenharmony_ci )) != msg[i].len) { 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci if (dvb_usb_opera1_debug & 0x10) 1518c2ecf20Sopenharmony_ci info("sending i2c message %d %d", tmp, msg[i].len); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci mutex_unlock(&d->i2c_mutex); 1548c2ecf20Sopenharmony_ci return num; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic u32 opera1_i2c_func(struct i2c_adapter *adapter) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci return I2C_FUNC_I2C; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic struct i2c_algorithm opera1_i2c_algo = { 1638c2ecf20Sopenharmony_ci .master_xfer = opera1_i2c_xfer, 1648c2ecf20Sopenharmony_ci .functionality = opera1_i2c_func, 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int opera1_set_voltage(struct dvb_frontend *fe, 1688c2ecf20Sopenharmony_ci enum fe_sec_voltage voltage) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci static u8 command_13v[1]={0x00}; 1718c2ecf20Sopenharmony_ci static u8 command_18v[1]={0x01}; 1728c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 1738c2ecf20Sopenharmony_ci {.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1}, 1748c2ecf20Sopenharmony_ci }; 1758c2ecf20Sopenharmony_ci struct dvb_usb_adapter *udev_adap = 1768c2ecf20Sopenharmony_ci (struct dvb_usb_adapter *)(fe->dvb->priv); 1778c2ecf20Sopenharmony_ci if (voltage == SEC_VOLTAGE_18) { 1788c2ecf20Sopenharmony_ci msg[0].addr = ADDR_B601_VOLTAGE_18V; 1798c2ecf20Sopenharmony_ci msg[0].buf = command_18v; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1); 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, 1868c2ecf20Sopenharmony_ci u32 ratio) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci stv0299_writereg(fe, 0x13, 0x98); 1898c2ecf20Sopenharmony_ci stv0299_writereg(fe, 0x14, 0x95); 1908c2ecf20Sopenharmony_ci stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, (ratio >> 16) & 0xff); 1918c2ecf20Sopenharmony_ci stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, (ratio >> 8) & 0xff); 1928c2ecf20Sopenharmony_ci stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, (ratio) & 0xf0); 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_cistatic u8 opera1_inittab[] = { 1978c2ecf20Sopenharmony_ci 0x00, 0xa1, 1988c2ecf20Sopenharmony_ci 0x01, 0x15, 1998c2ecf20Sopenharmony_ci 0x02, 0x30, 2008c2ecf20Sopenharmony_ci 0x03, 0x00, 2018c2ecf20Sopenharmony_ci 0x04, 0x7d, 2028c2ecf20Sopenharmony_ci 0x05, 0x05, 2038c2ecf20Sopenharmony_ci 0x06, 0x02, 2048c2ecf20Sopenharmony_ci 0x07, 0x00, 2058c2ecf20Sopenharmony_ci 0x0b, 0x00, 2068c2ecf20Sopenharmony_ci 0x0c, 0x01, 2078c2ecf20Sopenharmony_ci 0x0d, 0x81, 2088c2ecf20Sopenharmony_ci 0x0e, 0x44, 2098c2ecf20Sopenharmony_ci 0x0f, 0x19, 2108c2ecf20Sopenharmony_ci 0x10, 0x3f, 2118c2ecf20Sopenharmony_ci 0x11, 0x84, 2128c2ecf20Sopenharmony_ci 0x12, 0xda, 2138c2ecf20Sopenharmony_ci 0x13, 0x98, 2148c2ecf20Sopenharmony_ci 0x14, 0x95, 2158c2ecf20Sopenharmony_ci 0x15, 0xc9, 2168c2ecf20Sopenharmony_ci 0x16, 0xeb, 2178c2ecf20Sopenharmony_ci 0x17, 0x00, 2188c2ecf20Sopenharmony_ci 0x18, 0x19, 2198c2ecf20Sopenharmony_ci 0x19, 0x8b, 2208c2ecf20Sopenharmony_ci 0x1a, 0x00, 2218c2ecf20Sopenharmony_ci 0x1b, 0x82, 2228c2ecf20Sopenharmony_ci 0x1c, 0x7f, 2238c2ecf20Sopenharmony_ci 0x1d, 0x00, 2248c2ecf20Sopenharmony_ci 0x1e, 0x00, 2258c2ecf20Sopenharmony_ci REG_1F_SYMBOLRATE_BYTE0, 0x06, 2268c2ecf20Sopenharmony_ci REG_20_SYMBOLRATE_BYTE1, 0x50, 2278c2ecf20Sopenharmony_ci REG_21_SYMBOLRATE_BYTE2, 0x10, 2288c2ecf20Sopenharmony_ci 0x22, 0x00, 2298c2ecf20Sopenharmony_ci 0x23, 0x00, 2308c2ecf20Sopenharmony_ci 0x24, 0x37, 2318c2ecf20Sopenharmony_ci 0x25, 0xbc, 2328c2ecf20Sopenharmony_ci 0x26, 0x00, 2338c2ecf20Sopenharmony_ci 0x27, 0x00, 2348c2ecf20Sopenharmony_ci 0x28, 0x00, 2358c2ecf20Sopenharmony_ci 0x29, 0x1e, 2368c2ecf20Sopenharmony_ci 0x2a, 0x14, 2378c2ecf20Sopenharmony_ci 0x2b, 0x1f, 2388c2ecf20Sopenharmony_ci 0x2c, 0x09, 2398c2ecf20Sopenharmony_ci 0x2d, 0x0a, 2408c2ecf20Sopenharmony_ci 0x2e, 0x00, 2418c2ecf20Sopenharmony_ci 0x2f, 0x00, 2428c2ecf20Sopenharmony_ci 0x30, 0x00, 2438c2ecf20Sopenharmony_ci 0x31, 0x1f, 2448c2ecf20Sopenharmony_ci 0x32, 0x19, 2458c2ecf20Sopenharmony_ci 0x33, 0xfc, 2468c2ecf20Sopenharmony_ci 0x34, 0x13, 2478c2ecf20Sopenharmony_ci 0xff, 0xff, 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic struct stv0299_config opera1_stv0299_config = { 2518c2ecf20Sopenharmony_ci .demod_address = 0xd0>>1, 2528c2ecf20Sopenharmony_ci .min_delay_ms = 100, 2538c2ecf20Sopenharmony_ci .mclk = 88000000UL, 2548c2ecf20Sopenharmony_ci .invert = 1, 2558c2ecf20Sopenharmony_ci .skip_reinit = 0, 2568c2ecf20Sopenharmony_ci .lock_output = STV0299_LOCKOUTPUT_0, 2578c2ecf20Sopenharmony_ci .volt13_op0_op1 = STV0299_VOLT13_OP0, 2588c2ecf20Sopenharmony_ci .inittab = opera1_inittab, 2598c2ecf20Sopenharmony_ci .set_symbol_rate = opera1_stv0299_set_symbol_rate, 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int opera1_frontend_attach(struct dvb_usb_adapter *d) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci d->fe_adap[0].fe = dvb_attach(stv0299_attach, &opera1_stv0299_config, 2658c2ecf20Sopenharmony_ci &d->dev->i2c_adap); 2668c2ecf20Sopenharmony_ci if ((d->fe_adap[0].fe) != NULL) { 2678c2ecf20Sopenharmony_ci d->fe_adap[0].fe->ops.set_voltage = opera1_set_voltage; 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci info("not attached stv0299"); 2718c2ecf20Sopenharmony_ci return -EIO; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int opera1_tuner_attach(struct dvb_usb_adapter *adap) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci dvb_attach( 2778c2ecf20Sopenharmony_ci dvb_pll_attach, adap->fe_adap[0].fe, 0xc0>>1, 2788c2ecf20Sopenharmony_ci &adap->dev->i2c_adap, DVB_PLL_OPERA1 2798c2ecf20Sopenharmony_ci ); 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int opera1_power_ctrl(struct dvb_usb_device *d, int onoff) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci u8 val = onoff ? 0x01 : 0x00; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (dvb_usb_opera1_debug) 2888c2ecf20Sopenharmony_ci info("power %s", onoff ? "on" : "off"); 2898c2ecf20Sopenharmony_ci return opera1_xilinx_rw(d->udev, 0xb7, val, 2908c2ecf20Sopenharmony_ci &val, 1, OPERA_WRITE_MSG); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci static u8 buf_start[2] = { 0xff, 0x03 }; 2968c2ecf20Sopenharmony_ci static u8 buf_stop[2] = { 0xff, 0x00 }; 2978c2ecf20Sopenharmony_ci struct i2c_msg start_tuner[] = { 2988c2ecf20Sopenharmony_ci {.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2}, 2998c2ecf20Sopenharmony_ci }; 3008c2ecf20Sopenharmony_ci if (dvb_usb_opera1_debug) 3018c2ecf20Sopenharmony_ci info("streaming %s", onoff ? "on" : "off"); 3028c2ecf20Sopenharmony_ci i2c_transfer(&adap->dev->i2c_adap, start_tuner, 1); 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, 3078c2ecf20Sopenharmony_ci int onoff) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci u8 b_pid[3]; 3108c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 3118c2ecf20Sopenharmony_ci {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, 3128c2ecf20Sopenharmony_ci }; 3138c2ecf20Sopenharmony_ci if (dvb_usb_opera1_debug) 3148c2ecf20Sopenharmony_ci info("pidfilter index: %d pid: %d %s", index, pid, 3158c2ecf20Sopenharmony_ci onoff ? "on" : "off"); 3168c2ecf20Sopenharmony_ci b_pid[0] = (2 * index) + 4; 3178c2ecf20Sopenharmony_ci b_pid[1] = onoff ? (pid & 0xff) : (0x00); 3188c2ecf20Sopenharmony_ci b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00); 3198c2ecf20Sopenharmony_ci i2c_transfer(&adap->dev->i2c_adap, msg, 1); 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci int u = 0x04; 3268c2ecf20Sopenharmony_ci u8 b_pid[3]; 3278c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 3288c2ecf20Sopenharmony_ci {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, 3298c2ecf20Sopenharmony_ci }; 3308c2ecf20Sopenharmony_ci if (dvb_usb_opera1_debug) 3318c2ecf20Sopenharmony_ci info("%s hw-pidfilter", onoff ? "enable" : "disable"); 3328c2ecf20Sopenharmony_ci for (; u < 0x7e; u += 2) { 3338c2ecf20Sopenharmony_ci b_pid[0] = u; 3348c2ecf20Sopenharmony_ci b_pid[1] = 0; 3358c2ecf20Sopenharmony_ci b_pid[2] = 0x80; 3368c2ecf20Sopenharmony_ci i2c_transfer(&adap->dev->i2c_adap, msg, 1); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic struct rc_map_table rc_map_opera1_table[] = { 3428c2ecf20Sopenharmony_ci {0x5fa0, KEY_1}, 3438c2ecf20Sopenharmony_ci {0x51af, KEY_2}, 3448c2ecf20Sopenharmony_ci {0x5da2, KEY_3}, 3458c2ecf20Sopenharmony_ci {0x41be, KEY_4}, 3468c2ecf20Sopenharmony_ci {0x0bf5, KEY_5}, 3478c2ecf20Sopenharmony_ci {0x43bd, KEY_6}, 3488c2ecf20Sopenharmony_ci {0x47b8, KEY_7}, 3498c2ecf20Sopenharmony_ci {0x49b6, KEY_8}, 3508c2ecf20Sopenharmony_ci {0x05fa, KEY_9}, 3518c2ecf20Sopenharmony_ci {0x45ba, KEY_0}, 3528c2ecf20Sopenharmony_ci {0x09f6, KEY_CHANNELUP}, /*chanup */ 3538c2ecf20Sopenharmony_ci {0x1be5, KEY_CHANNELDOWN}, /*chandown */ 3548c2ecf20Sopenharmony_ci {0x5da3, KEY_VOLUMEDOWN}, /*voldown */ 3558c2ecf20Sopenharmony_ci {0x5fa1, KEY_VOLUMEUP}, /*volup */ 3568c2ecf20Sopenharmony_ci {0x07f8, KEY_SPACE}, /*tab */ 3578c2ecf20Sopenharmony_ci {0x1fe1, KEY_OK}, /*play ok */ 3588c2ecf20Sopenharmony_ci {0x1be4, KEY_ZOOM}, /*zoom */ 3598c2ecf20Sopenharmony_ci {0x59a6, KEY_MUTE}, /*mute */ 3608c2ecf20Sopenharmony_ci {0x5ba5, KEY_RADIO}, /*tv/f */ 3618c2ecf20Sopenharmony_ci {0x19e7, KEY_RECORD}, /*rec */ 3628c2ecf20Sopenharmony_ci {0x01fe, KEY_STOP}, /*Stop */ 3638c2ecf20Sopenharmony_ci {0x03fd, KEY_PAUSE}, /*pause */ 3648c2ecf20Sopenharmony_ci {0x03fc, KEY_SCREEN}, /*<- -> */ 3658c2ecf20Sopenharmony_ci {0x07f9, KEY_CAMERA}, /*capture */ 3668c2ecf20Sopenharmony_ci {0x47b9, KEY_ESC}, /*exit */ 3678c2ecf20Sopenharmony_ci {0x43bc, KEY_POWER2}, /*power */ 3688c2ecf20Sopenharmony_ci}; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct opera1_state *opst = dev->priv; 3738c2ecf20Sopenharmony_ci u8 rcbuffer[32]; 3748c2ecf20Sopenharmony_ci const u16 startmarker1 = 0x10ed; 3758c2ecf20Sopenharmony_ci const u16 startmarker2 = 0x11ec; 3768c2ecf20Sopenharmony_ci struct i2c_msg read_remote[] = { 3778c2ecf20Sopenharmony_ci {.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32}, 3788c2ecf20Sopenharmony_ci }; 3798c2ecf20Sopenharmony_ci int i = 0; 3808c2ecf20Sopenharmony_ci u32 send_key = 0; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (i2c_transfer(&dev->i2c_adap, read_remote, 1) == 1) { 3838c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) { 3848c2ecf20Sopenharmony_ci if (rcbuffer[i]) 3858c2ecf20Sopenharmony_ci send_key |= 1; 3868c2ecf20Sopenharmony_ci if (i < 31) 3878c2ecf20Sopenharmony_ci send_key = send_key << 1; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci if (send_key & 0x8000) 3908c2ecf20Sopenharmony_ci send_key = (send_key << 1) | (send_key >> 15 & 0x01); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (send_key == 0xffff && opst->last_key_pressed != 0) { 3938c2ecf20Sopenharmony_ci *state = REMOTE_KEY_REPEAT; 3948c2ecf20Sopenharmony_ci *event = opst->last_key_pressed; 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci for (; send_key != 0;) { 3988c2ecf20Sopenharmony_ci if (send_key >> 16 == startmarker2) { 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci } else if (send_key >> 16 == startmarker1) { 4018c2ecf20Sopenharmony_ci send_key = 4028c2ecf20Sopenharmony_ci (send_key & 0xfffeffff) | (startmarker1 << 16); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci } else 4058c2ecf20Sopenharmony_ci send_key >>= 1; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (send_key == 0) 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci send_key = (send_key & 0xffff) | 0x0100; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rc_map_opera1_table); i++) { 4148c2ecf20Sopenharmony_ci if (rc5_scan(&rc_map_opera1_table[i]) == (send_key & 0xffff)) { 4158c2ecf20Sopenharmony_ci *state = REMOTE_KEY_PRESSED; 4168c2ecf20Sopenharmony_ci *event = rc_map_opera1_table[i].keycode; 4178c2ecf20Sopenharmony_ci opst->last_key_pressed = 4188c2ecf20Sopenharmony_ci rc_map_opera1_table[i].keycode; 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci opst->last_key_pressed = 0; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci } else 4248c2ecf20Sopenharmony_ci *state = REMOTE_NO_KEY_PRESSED; 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic struct usb_device_id opera1_table[] = { 4298c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VID_CYPRESS, USB_PID_OPERA1_COLD)}, 4308c2ecf20Sopenharmony_ci {USB_DEVICE(USB_VID_OPERA1, USB_PID_OPERA1_WARM)}, 4318c2ecf20Sopenharmony_ci {} 4328c2ecf20Sopenharmony_ci}; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, opera1_table); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci u8 command[] = { READ_MAC_ADDR }; 4398c2ecf20Sopenharmony_ci opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG); 4408c2ecf20Sopenharmony_ci opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG); 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_cistatic int opera1_xilinx_load_firmware(struct usb_device *dev, 4448c2ecf20Sopenharmony_ci const char *filename) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci const struct firmware *fw = NULL; 4478c2ecf20Sopenharmony_ci u8 *b, *p; 4488c2ecf20Sopenharmony_ci int ret = 0, i,fpgasize=40; 4498c2ecf20Sopenharmony_ci u8 testval; 4508c2ecf20Sopenharmony_ci info("start downloading fpga firmware %s",filename); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) { 4538c2ecf20Sopenharmony_ci err("did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware", 4548c2ecf20Sopenharmony_ci filename); 4558c2ecf20Sopenharmony_ci return ret; 4568c2ecf20Sopenharmony_ci } else { 4578c2ecf20Sopenharmony_ci p = kmalloc(fw->size, GFP_KERNEL); 4588c2ecf20Sopenharmony_ci opera1_xilinx_rw(dev, 0xbc, 0x00, &testval, 1, OPERA_READ_MSG); 4598c2ecf20Sopenharmony_ci if (p != NULL && testval != 0x67) { 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci u8 reset = 0, fpga_command = 0; 4628c2ecf20Sopenharmony_ci memcpy(p, fw->data, fw->size); 4638c2ecf20Sopenharmony_ci /* clear fpga ? */ 4648c2ecf20Sopenharmony_ci opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1, 4658c2ecf20Sopenharmony_ci OPERA_WRITE_MSG); 4668c2ecf20Sopenharmony_ci for (i = 0; i < fw->size;) { 4678c2ecf20Sopenharmony_ci if ( (fw->size - i) <fpgasize){ 4688c2ecf20Sopenharmony_ci fpgasize=fw->size-i; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci b = (u8 *) p + i; 4718c2ecf20Sopenharmony_ci if (opera1_xilinx_rw 4728c2ecf20Sopenharmony_ci (dev, OPERA_WRITE_FX2, 0x0, b , fpgasize, 4738c2ecf20Sopenharmony_ci OPERA_WRITE_MSG) != fpgasize 4748c2ecf20Sopenharmony_ci ) { 4758c2ecf20Sopenharmony_ci err("error while transferring firmware"); 4768c2ecf20Sopenharmony_ci ret = -EINVAL; 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci i = i + fpgasize; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci /* restart the CPU */ 4828c2ecf20Sopenharmony_ci if (ret || opera1_xilinx_rw 4838c2ecf20Sopenharmony_ci (dev, 0xa0, 0xe600, &reset, 1, 4848c2ecf20Sopenharmony_ci OPERA_WRITE_MSG) != 1) { 4858c2ecf20Sopenharmony_ci err("could not restart the USB controller CPU."); 4868c2ecf20Sopenharmony_ci ret = -EINVAL; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci kfree(p); 4918c2ecf20Sopenharmony_ci release_firmware(fw); 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic struct dvb_usb_device_properties opera1_properties = { 4968c2ecf20Sopenharmony_ci .caps = DVB_USB_IS_AN_I2C_ADAPTER, 4978c2ecf20Sopenharmony_ci .usb_ctrl = CYPRESS_FX2, 4988c2ecf20Sopenharmony_ci .firmware = "dvb-usb-opera-01.fw", 4998c2ecf20Sopenharmony_ci .size_of_priv = sizeof(struct opera1_state), 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci .power_ctrl = opera1_power_ctrl, 5028c2ecf20Sopenharmony_ci .i2c_algo = &opera1_i2c_algo, 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci .rc.legacy = { 5058c2ecf20Sopenharmony_ci .rc_map_table = rc_map_opera1_table, 5068c2ecf20Sopenharmony_ci .rc_map_size = ARRAY_SIZE(rc_map_opera1_table), 5078c2ecf20Sopenharmony_ci .rc_interval = 200, 5088c2ecf20Sopenharmony_ci .rc_query = opera1_rc_query, 5098c2ecf20Sopenharmony_ci }, 5108c2ecf20Sopenharmony_ci .read_mac_address = opera1_read_mac_address, 5118c2ecf20Sopenharmony_ci .generic_bulk_ctrl_endpoint = 0x00, 5128c2ecf20Sopenharmony_ci /* parameter for the MPEG2-data transfer */ 5138c2ecf20Sopenharmony_ci .num_adapters = 1, 5148c2ecf20Sopenharmony_ci .adapter = { 5158c2ecf20Sopenharmony_ci { 5168c2ecf20Sopenharmony_ci .num_frontends = 1, 5178c2ecf20Sopenharmony_ci .fe = {{ 5188c2ecf20Sopenharmony_ci .frontend_attach = opera1_frontend_attach, 5198c2ecf20Sopenharmony_ci .streaming_ctrl = opera1_streaming_ctrl, 5208c2ecf20Sopenharmony_ci .tuner_attach = opera1_tuner_attach, 5218c2ecf20Sopenharmony_ci .caps = 5228c2ecf20Sopenharmony_ci DVB_USB_ADAP_HAS_PID_FILTER | 5238c2ecf20Sopenharmony_ci DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, 5248c2ecf20Sopenharmony_ci .pid_filter = opera1_pid_filter, 5258c2ecf20Sopenharmony_ci .pid_filter_ctrl = opera1_pid_filter_control, 5268c2ecf20Sopenharmony_ci .pid_filter_count = 252, 5278c2ecf20Sopenharmony_ci .stream = { 5288c2ecf20Sopenharmony_ci .type = USB_BULK, 5298c2ecf20Sopenharmony_ci .count = 10, 5308c2ecf20Sopenharmony_ci .endpoint = 0x82, 5318c2ecf20Sopenharmony_ci .u = { 5328c2ecf20Sopenharmony_ci .bulk = { 5338c2ecf20Sopenharmony_ci .buffersize = 4096, 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci }, 5378c2ecf20Sopenharmony_ci }}, 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci }, 5408c2ecf20Sopenharmony_ci .num_device_descs = 1, 5418c2ecf20Sopenharmony_ci .devices = { 5428c2ecf20Sopenharmony_ci {"Opera1 DVB-S USB2.0", 5438c2ecf20Sopenharmony_ci {&opera1_table[0], NULL}, 5448c2ecf20Sopenharmony_ci {&opera1_table[1], NULL}, 5458c2ecf20Sopenharmony_ci }, 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci}; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int opera1_probe(struct usb_interface *intf, 5508c2ecf20Sopenharmony_ci const struct usb_device_id *id) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (le16_to_cpu(udev->descriptor.idProduct) == USB_PID_OPERA1_WARM && 5558c2ecf20Sopenharmony_ci le16_to_cpu(udev->descriptor.idVendor) == USB_VID_OPERA1 && 5568c2ecf20Sopenharmony_ci opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga-01.fw") != 0 5578c2ecf20Sopenharmony_ci ) { 5588c2ecf20Sopenharmony_ci return -EINVAL; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (0 != dvb_usb_device_init(intf, &opera1_properties, 5628c2ecf20Sopenharmony_ci THIS_MODULE, NULL, adapter_nr)) 5638c2ecf20Sopenharmony_ci return -EINVAL; 5648c2ecf20Sopenharmony_ci return 0; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic struct usb_driver opera1_driver = { 5688c2ecf20Sopenharmony_ci .name = "opera1", 5698c2ecf20Sopenharmony_ci .probe = opera1_probe, 5708c2ecf20Sopenharmony_ci .disconnect = dvb_usb_device_exit, 5718c2ecf20Sopenharmony_ci .id_table = opera1_table, 5728c2ecf20Sopenharmony_ci}; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cimodule_usb_driver(opera1_driver); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org"); 5778c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de"); 5788c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Opera1 DVB-S device"); 5798c2ecf20Sopenharmony_ciMODULE_VERSION("0.1"); 5808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 581