xref: /openbmc/linux/drivers/media/usb/dvb-usb/opera1.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2786baecfSMauro Carvalho Chehab /* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card
3786baecfSMauro Carvalho Chehab *
4786baecfSMauro Carvalho Chehab * Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org)
5786baecfSMauro Carvalho Chehab * Copyright (C) 2006 Marco Gittler (g.marco@freenet.de)
6786baecfSMauro Carvalho Chehab *
7577a7ad3SMauro Carvalho Chehab * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information
8786baecfSMauro Carvalho Chehab */
9786baecfSMauro Carvalho Chehab 
10786baecfSMauro Carvalho Chehab #define DVB_USB_LOG_PREFIX "opera"
11786baecfSMauro Carvalho Chehab 
12786baecfSMauro Carvalho Chehab #include "dvb-usb.h"
13786baecfSMauro Carvalho Chehab #include "stv0299.h"
14786baecfSMauro Carvalho Chehab 
15786baecfSMauro Carvalho Chehab #define OPERA_READ_MSG 0
16786baecfSMauro Carvalho Chehab #define OPERA_WRITE_MSG 1
17786baecfSMauro Carvalho Chehab #define OPERA_I2C_TUNER 0xd1
18786baecfSMauro Carvalho Chehab 
19786baecfSMauro Carvalho Chehab #define READ_FX2_REG_REQ  0xba
20786baecfSMauro Carvalho Chehab #define READ_MAC_ADDR 0x08
21786baecfSMauro Carvalho Chehab #define OPERA_WRITE_FX2 0xbb
22786baecfSMauro Carvalho Chehab #define OPERA_TUNER_REQ 0xb1
23786baecfSMauro Carvalho Chehab #define REG_1F_SYMBOLRATE_BYTE0 0x1f
24786baecfSMauro Carvalho Chehab #define REG_20_SYMBOLRATE_BYTE1 0x20
25786baecfSMauro Carvalho Chehab #define REG_21_SYMBOLRATE_BYTE2 0x21
26786baecfSMauro Carvalho Chehab 
27786baecfSMauro Carvalho Chehab #define ADDR_B600_VOLTAGE_13V (0x02)
28786baecfSMauro Carvalho Chehab #define ADDR_B601_VOLTAGE_18V (0x03)
29786baecfSMauro Carvalho Chehab #define ADDR_B1A6_STREAM_CTRL (0x04)
30786baecfSMauro Carvalho Chehab #define ADDR_B880_READ_REMOTE (0x05)
31786baecfSMauro Carvalho Chehab 
32786baecfSMauro Carvalho Chehab struct opera1_state {
33786baecfSMauro Carvalho Chehab 	u32 last_key_pressed;
34786baecfSMauro Carvalho Chehab };
35786baecfSMauro Carvalho Chehab struct rc_map_opera_table {
36786baecfSMauro Carvalho Chehab 	u32 keycode;
37786baecfSMauro Carvalho Chehab 	u32 event;
38786baecfSMauro Carvalho Chehab };
39786baecfSMauro Carvalho Chehab 
40786baecfSMauro Carvalho Chehab static int dvb_usb_opera1_debug;
41786baecfSMauro Carvalho Chehab module_param_named(debug, dvb_usb_opera1_debug, int, 0644);
42786baecfSMauro Carvalho Chehab MODULE_PARM_DESC(debug,
43786baecfSMauro Carvalho Chehab 		 "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))."
44786baecfSMauro Carvalho Chehab 		 DVB_USB_DEBUG_STATUS);
45786baecfSMauro Carvalho Chehab 
46786baecfSMauro Carvalho Chehab DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
47786baecfSMauro Carvalho Chehab 
48786baecfSMauro Carvalho Chehab 
opera1_xilinx_rw(struct usb_device * dev,u8 request,u16 value,u8 * data,u16 len,int flags)49786baecfSMauro Carvalho Chehab static int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value,
50786baecfSMauro Carvalho Chehab 			    u8 * data, u16 len, int flags)
51786baecfSMauro Carvalho Chehab {
52786baecfSMauro Carvalho Chehab 	int ret;
53786baecfSMauro Carvalho Chehab 	u8 tmp;
54786baecfSMauro Carvalho Chehab 	u8 *buf;
55786baecfSMauro Carvalho Chehab 	unsigned int pipe = (flags == OPERA_READ_MSG) ?
56786baecfSMauro Carvalho Chehab 		usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0);
57786baecfSMauro Carvalho Chehab 	u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
58786baecfSMauro Carvalho Chehab 
59786baecfSMauro Carvalho Chehab 	buf = kmalloc(len, GFP_KERNEL);
60786baecfSMauro Carvalho Chehab 	if (!buf)
61786baecfSMauro Carvalho Chehab 		return -ENOMEM;
62786baecfSMauro Carvalho Chehab 
63786baecfSMauro Carvalho Chehab 	if (flags == OPERA_WRITE_MSG)
64786baecfSMauro Carvalho Chehab 		memcpy(buf, data, len);
65786baecfSMauro Carvalho Chehab 	ret = usb_control_msg(dev, pipe, request,
66786baecfSMauro Carvalho Chehab 			request_type | USB_TYPE_VENDOR, value, 0x0,
67786baecfSMauro Carvalho Chehab 			buf, len, 2000);
68786baecfSMauro Carvalho Chehab 
69786baecfSMauro Carvalho Chehab 	if (request == OPERA_TUNER_REQ) {
70786baecfSMauro Carvalho Chehab 		tmp = buf[0];
71786baecfSMauro Carvalho Chehab 		if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
72786baecfSMauro Carvalho Chehab 			    OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR,
73786baecfSMauro Carvalho Chehab 			    0x01, 0x0, buf, 1, 2000) < 1 || buf[0] != 0x08) {
74786baecfSMauro Carvalho Chehab 			ret = 0;
75786baecfSMauro Carvalho Chehab 			goto out;
76786baecfSMauro Carvalho Chehab 		}
77786baecfSMauro Carvalho Chehab 		buf[0] = tmp;
78786baecfSMauro Carvalho Chehab 	}
79786baecfSMauro Carvalho Chehab 	if (flags == OPERA_READ_MSG)
80786baecfSMauro Carvalho Chehab 		memcpy(data, buf, len);
81786baecfSMauro Carvalho Chehab out:
82786baecfSMauro Carvalho Chehab 	kfree(buf);
83786baecfSMauro Carvalho Chehab 	return ret;
84786baecfSMauro Carvalho Chehab }
85786baecfSMauro Carvalho Chehab 
86786baecfSMauro Carvalho Chehab /* I2C */
87786baecfSMauro Carvalho Chehab 
opera1_usb_i2c_msgxfer(struct dvb_usb_device * dev,u16 addr,u8 * buf,u16 len)88786baecfSMauro Carvalho Chehab static int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr,
89786baecfSMauro Carvalho Chehab 				  u8 * buf, u16 len)
90786baecfSMauro Carvalho Chehab {
91786baecfSMauro Carvalho Chehab 	int ret = 0;
92786baecfSMauro Carvalho Chehab 	u8 request;
93786baecfSMauro Carvalho Chehab 	u16 value;
94786baecfSMauro Carvalho Chehab 
95786baecfSMauro Carvalho Chehab 	if (!dev) {
96786baecfSMauro Carvalho Chehab 		info("no usb_device");
97786baecfSMauro Carvalho Chehab 		return -EINVAL;
98786baecfSMauro Carvalho Chehab 	}
99786baecfSMauro Carvalho Chehab 	if (mutex_lock_interruptible(&dev->usb_mutex) < 0)
100786baecfSMauro Carvalho Chehab 		return -EAGAIN;
101786baecfSMauro Carvalho Chehab 
102786baecfSMauro Carvalho Chehab 	switch (addr>>1){
103786baecfSMauro Carvalho Chehab 		case ADDR_B600_VOLTAGE_13V:
104786baecfSMauro Carvalho Chehab 			request=0xb6;
105786baecfSMauro Carvalho Chehab 			value=0x00;
106786baecfSMauro Carvalho Chehab 			break;
107786baecfSMauro Carvalho Chehab 		case ADDR_B601_VOLTAGE_18V:
108786baecfSMauro Carvalho Chehab 			request=0xb6;
109786baecfSMauro Carvalho Chehab 			value=0x01;
110786baecfSMauro Carvalho Chehab 			break;
111786baecfSMauro Carvalho Chehab 		case ADDR_B1A6_STREAM_CTRL:
112786baecfSMauro Carvalho Chehab 			request=0xb1;
113786baecfSMauro Carvalho Chehab 			value=0xa6;
114786baecfSMauro Carvalho Chehab 			break;
115786baecfSMauro Carvalho Chehab 		case ADDR_B880_READ_REMOTE:
116786baecfSMauro Carvalho Chehab 			request=0xb8;
117786baecfSMauro Carvalho Chehab 			value=0x80;
118786baecfSMauro Carvalho Chehab 			break;
119786baecfSMauro Carvalho Chehab 		default:
120786baecfSMauro Carvalho Chehab 			request=0xb1;
121786baecfSMauro Carvalho Chehab 			value=addr;
122786baecfSMauro Carvalho Chehab 	}
123786baecfSMauro Carvalho Chehab 	ret = opera1_xilinx_rw(dev->udev, request,
124786baecfSMauro Carvalho Chehab 		value, buf, len,
125786baecfSMauro Carvalho Chehab 		addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG);
126786baecfSMauro Carvalho Chehab 
127786baecfSMauro Carvalho Chehab 	mutex_unlock(&dev->usb_mutex);
128786baecfSMauro Carvalho Chehab 	return ret;
129786baecfSMauro Carvalho Chehab }
130786baecfSMauro Carvalho Chehab 
opera1_i2c_xfer(struct i2c_adapter * adap,struct i2c_msg msg[],int num)131786baecfSMauro Carvalho Chehab static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
132786baecfSMauro Carvalho Chehab 			   int num)
133786baecfSMauro Carvalho Chehab {
134786baecfSMauro Carvalho Chehab 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
135786baecfSMauro Carvalho Chehab 	int i = 0, tmp = 0;
136786baecfSMauro Carvalho Chehab 
137786baecfSMauro Carvalho Chehab 	if (!d)
138786baecfSMauro Carvalho Chehab 		return -ENODEV;
139786baecfSMauro Carvalho Chehab 	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
140786baecfSMauro Carvalho Chehab 		return -EAGAIN;
141786baecfSMauro Carvalho Chehab 
142786baecfSMauro Carvalho Chehab 	for (i = 0; i < num; i++) {
143786baecfSMauro Carvalho Chehab 		if ((tmp = opera1_usb_i2c_msgxfer(d,
144786baecfSMauro Carvalho Chehab 					(msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0),
145786baecfSMauro Carvalho Chehab 					msg[i].buf,
146786baecfSMauro Carvalho Chehab 					msg[i].len
147786baecfSMauro Carvalho Chehab 					)) != msg[i].len) {
148786baecfSMauro Carvalho Chehab 			break;
149786baecfSMauro Carvalho Chehab 		}
150786baecfSMauro Carvalho Chehab 		if (dvb_usb_opera1_debug & 0x10)
15107f42258SMasanari Iida 			info("sending i2c message %d %d", tmp, msg[i].len);
152786baecfSMauro Carvalho Chehab 	}
153786baecfSMauro Carvalho Chehab 	mutex_unlock(&d->i2c_mutex);
154786baecfSMauro Carvalho Chehab 	return num;
155786baecfSMauro Carvalho Chehab }
156786baecfSMauro Carvalho Chehab 
opera1_i2c_func(struct i2c_adapter * adapter)157786baecfSMauro Carvalho Chehab static u32 opera1_i2c_func(struct i2c_adapter *adapter)
158786baecfSMauro Carvalho Chehab {
159786baecfSMauro Carvalho Chehab 	return I2C_FUNC_I2C;
160786baecfSMauro Carvalho Chehab }
161786baecfSMauro Carvalho Chehab 
162786baecfSMauro Carvalho Chehab static struct i2c_algorithm opera1_i2c_algo = {
163786baecfSMauro Carvalho Chehab 	.master_xfer = opera1_i2c_xfer,
164786baecfSMauro Carvalho Chehab 	.functionality = opera1_i2c_func,
165786baecfSMauro Carvalho Chehab };
166786baecfSMauro Carvalho Chehab 
opera1_set_voltage(struct dvb_frontend * fe,enum fe_sec_voltage voltage)1670df289a2SMauro Carvalho Chehab static int opera1_set_voltage(struct dvb_frontend *fe,
1680df289a2SMauro Carvalho Chehab 			      enum fe_sec_voltage voltage)
169786baecfSMauro Carvalho Chehab {
170786baecfSMauro Carvalho Chehab 	static u8 command_13v[1]={0x00};
171786baecfSMauro Carvalho Chehab 	static u8 command_18v[1]={0x01};
172786baecfSMauro Carvalho Chehab 	struct i2c_msg msg[] = {
173786baecfSMauro Carvalho Chehab 		{.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1},
174786baecfSMauro Carvalho Chehab 	};
175663e7460SYu Zhe 	struct dvb_usb_adapter *udev_adap = fe->dvb->priv;
176786baecfSMauro Carvalho Chehab 	if (voltage == SEC_VOLTAGE_18) {
177786baecfSMauro Carvalho Chehab 		msg[0].addr = ADDR_B601_VOLTAGE_18V;
178786baecfSMauro Carvalho Chehab 		msg[0].buf = command_18v;
179786baecfSMauro Carvalho Chehab 	}
180786baecfSMauro Carvalho Chehab 	i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1);
181786baecfSMauro Carvalho Chehab 	return 0;
182786baecfSMauro Carvalho Chehab }
183786baecfSMauro Carvalho Chehab 
opera1_stv0299_set_symbol_rate(struct dvb_frontend * fe,u32 srate,u32 ratio)184786baecfSMauro Carvalho Chehab static int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate,
185786baecfSMauro Carvalho Chehab 					  u32 ratio)
186786baecfSMauro Carvalho Chehab {
187786baecfSMauro Carvalho Chehab 	stv0299_writereg(fe, 0x13, 0x98);
188786baecfSMauro Carvalho Chehab 	stv0299_writereg(fe, 0x14, 0x95);
189786baecfSMauro Carvalho Chehab 	stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, (ratio >> 16) & 0xff);
190786baecfSMauro Carvalho Chehab 	stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, (ratio >> 8) & 0xff);
191786baecfSMauro Carvalho Chehab 	stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, (ratio) & 0xf0);
192786baecfSMauro Carvalho Chehab 	return 0;
193786baecfSMauro Carvalho Chehab 
194786baecfSMauro Carvalho Chehab }
195786baecfSMauro Carvalho Chehab static u8 opera1_inittab[] = {
196786baecfSMauro Carvalho Chehab 	0x00, 0xa1,
197786baecfSMauro Carvalho Chehab 	0x01, 0x15,
198786baecfSMauro Carvalho Chehab 	0x02, 0x30,
199786baecfSMauro Carvalho Chehab 	0x03, 0x00,
200786baecfSMauro Carvalho Chehab 	0x04, 0x7d,
201786baecfSMauro Carvalho Chehab 	0x05, 0x05,
202786baecfSMauro Carvalho Chehab 	0x06, 0x02,
203786baecfSMauro Carvalho Chehab 	0x07, 0x00,
204786baecfSMauro Carvalho Chehab 	0x0b, 0x00,
205786baecfSMauro Carvalho Chehab 	0x0c, 0x01,
206786baecfSMauro Carvalho Chehab 	0x0d, 0x81,
207786baecfSMauro Carvalho Chehab 	0x0e, 0x44,
208786baecfSMauro Carvalho Chehab 	0x0f, 0x19,
209786baecfSMauro Carvalho Chehab 	0x10, 0x3f,
210786baecfSMauro Carvalho Chehab 	0x11, 0x84,
211786baecfSMauro Carvalho Chehab 	0x12, 0xda,
212786baecfSMauro Carvalho Chehab 	0x13, 0x98,
213786baecfSMauro Carvalho Chehab 	0x14, 0x95,
214786baecfSMauro Carvalho Chehab 	0x15, 0xc9,
215786baecfSMauro Carvalho Chehab 	0x16, 0xeb,
216786baecfSMauro Carvalho Chehab 	0x17, 0x00,
217786baecfSMauro Carvalho Chehab 	0x18, 0x19,
218786baecfSMauro Carvalho Chehab 	0x19, 0x8b,
219786baecfSMauro Carvalho Chehab 	0x1a, 0x00,
220786baecfSMauro Carvalho Chehab 	0x1b, 0x82,
221786baecfSMauro Carvalho Chehab 	0x1c, 0x7f,
222786baecfSMauro Carvalho Chehab 	0x1d, 0x00,
223786baecfSMauro Carvalho Chehab 	0x1e, 0x00,
224786baecfSMauro Carvalho Chehab 	REG_1F_SYMBOLRATE_BYTE0, 0x06,
225786baecfSMauro Carvalho Chehab 	REG_20_SYMBOLRATE_BYTE1, 0x50,
226786baecfSMauro Carvalho Chehab 	REG_21_SYMBOLRATE_BYTE2, 0x10,
227786baecfSMauro Carvalho Chehab 	0x22, 0x00,
228786baecfSMauro Carvalho Chehab 	0x23, 0x00,
229786baecfSMauro Carvalho Chehab 	0x24, 0x37,
230786baecfSMauro Carvalho Chehab 	0x25, 0xbc,
231786baecfSMauro Carvalho Chehab 	0x26, 0x00,
232786baecfSMauro Carvalho Chehab 	0x27, 0x00,
233786baecfSMauro Carvalho Chehab 	0x28, 0x00,
234786baecfSMauro Carvalho Chehab 	0x29, 0x1e,
235786baecfSMauro Carvalho Chehab 	0x2a, 0x14,
236786baecfSMauro Carvalho Chehab 	0x2b, 0x1f,
237786baecfSMauro Carvalho Chehab 	0x2c, 0x09,
238786baecfSMauro Carvalho Chehab 	0x2d, 0x0a,
239786baecfSMauro Carvalho Chehab 	0x2e, 0x00,
240786baecfSMauro Carvalho Chehab 	0x2f, 0x00,
241786baecfSMauro Carvalho Chehab 	0x30, 0x00,
242786baecfSMauro Carvalho Chehab 	0x31, 0x1f,
243786baecfSMauro Carvalho Chehab 	0x32, 0x19,
244786baecfSMauro Carvalho Chehab 	0x33, 0xfc,
245786baecfSMauro Carvalho Chehab 	0x34, 0x13,
246786baecfSMauro Carvalho Chehab 	0xff, 0xff,
247786baecfSMauro Carvalho Chehab };
248786baecfSMauro Carvalho Chehab 
249786baecfSMauro Carvalho Chehab static struct stv0299_config opera1_stv0299_config = {
250786baecfSMauro Carvalho Chehab 	.demod_address = 0xd0>>1,
251786baecfSMauro Carvalho Chehab 	.min_delay_ms = 100,
252786baecfSMauro Carvalho Chehab 	.mclk = 88000000UL,
253786baecfSMauro Carvalho Chehab 	.invert = 1,
254786baecfSMauro Carvalho Chehab 	.skip_reinit = 0,
255786baecfSMauro Carvalho Chehab 	.lock_output = STV0299_LOCKOUTPUT_0,
256786baecfSMauro Carvalho Chehab 	.volt13_op0_op1 = STV0299_VOLT13_OP0,
257786baecfSMauro Carvalho Chehab 	.inittab = opera1_inittab,
258786baecfSMauro Carvalho Chehab 	.set_symbol_rate = opera1_stv0299_set_symbol_rate,
259786baecfSMauro Carvalho Chehab };
260786baecfSMauro Carvalho Chehab 
opera1_frontend_attach(struct dvb_usb_adapter * d)261786baecfSMauro Carvalho Chehab static int opera1_frontend_attach(struct dvb_usb_adapter *d)
262786baecfSMauro Carvalho Chehab {
263786baecfSMauro Carvalho Chehab 	d->fe_adap[0].fe = dvb_attach(stv0299_attach, &opera1_stv0299_config,
264786baecfSMauro Carvalho Chehab 				      &d->dev->i2c_adap);
265786baecfSMauro Carvalho Chehab 	if ((d->fe_adap[0].fe) != NULL) {
266786baecfSMauro Carvalho Chehab 		d->fe_adap[0].fe->ops.set_voltage = opera1_set_voltage;
267786baecfSMauro Carvalho Chehab 		return 0;
268786baecfSMauro Carvalho Chehab 	}
269786baecfSMauro Carvalho Chehab 	info("not attached stv0299");
270786baecfSMauro Carvalho Chehab 	return -EIO;
271786baecfSMauro Carvalho Chehab }
272786baecfSMauro Carvalho Chehab 
opera1_tuner_attach(struct dvb_usb_adapter * adap)273786baecfSMauro Carvalho Chehab static int opera1_tuner_attach(struct dvb_usb_adapter *adap)
274786baecfSMauro Carvalho Chehab {
275786baecfSMauro Carvalho Chehab 	dvb_attach(
276786baecfSMauro Carvalho Chehab 		dvb_pll_attach, adap->fe_adap[0].fe, 0xc0>>1,
277786baecfSMauro Carvalho Chehab 		&adap->dev->i2c_adap, DVB_PLL_OPERA1
278786baecfSMauro Carvalho Chehab 	);
279786baecfSMauro Carvalho Chehab 	return 0;
280786baecfSMauro Carvalho Chehab }
281786baecfSMauro Carvalho Chehab 
opera1_power_ctrl(struct dvb_usb_device * d,int onoff)282786baecfSMauro Carvalho Chehab static int opera1_power_ctrl(struct dvb_usb_device *d, int onoff)
283786baecfSMauro Carvalho Chehab {
284786baecfSMauro Carvalho Chehab 	u8 val = onoff ? 0x01 : 0x00;
285786baecfSMauro Carvalho Chehab 
286786baecfSMauro Carvalho Chehab 	if (dvb_usb_opera1_debug)
287786baecfSMauro Carvalho Chehab 		info("power %s", onoff ? "on" : "off");
288786baecfSMauro Carvalho Chehab 	return opera1_xilinx_rw(d->udev, 0xb7, val,
289786baecfSMauro Carvalho Chehab 				&val, 1, OPERA_WRITE_MSG);
290786baecfSMauro Carvalho Chehab }
291786baecfSMauro Carvalho Chehab 
opera1_streaming_ctrl(struct dvb_usb_adapter * adap,int onoff)292786baecfSMauro Carvalho Chehab static int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
293786baecfSMauro Carvalho Chehab {
294786baecfSMauro Carvalho Chehab 	static u8 buf_start[2] = { 0xff, 0x03 };
295786baecfSMauro Carvalho Chehab 	static u8 buf_stop[2] = { 0xff, 0x00 };
296786baecfSMauro Carvalho Chehab 	struct i2c_msg start_tuner[] = {
297786baecfSMauro Carvalho Chehab 		{.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2},
298786baecfSMauro Carvalho Chehab 	};
299786baecfSMauro Carvalho Chehab 	if (dvb_usb_opera1_debug)
300786baecfSMauro Carvalho Chehab 		info("streaming %s", onoff ? "on" : "off");
301786baecfSMauro Carvalho Chehab 	i2c_transfer(&adap->dev->i2c_adap, start_tuner, 1);
302786baecfSMauro Carvalho Chehab 	return 0;
303786baecfSMauro Carvalho Chehab }
304786baecfSMauro Carvalho Chehab 
opera1_pid_filter(struct dvb_usb_adapter * adap,int index,u16 pid,int onoff)305786baecfSMauro Carvalho Chehab static int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
306786baecfSMauro Carvalho Chehab 			     int onoff)
307786baecfSMauro Carvalho Chehab {
308786baecfSMauro Carvalho Chehab 	u8 b_pid[3];
309786baecfSMauro Carvalho Chehab 	struct i2c_msg msg[] = {
310786baecfSMauro Carvalho Chehab 		{.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3},
311786baecfSMauro Carvalho Chehab 	};
312786baecfSMauro Carvalho Chehab 	if (dvb_usb_opera1_debug)
313786baecfSMauro Carvalho Chehab 		info("pidfilter index: %d pid: %d %s", index, pid,
314786baecfSMauro Carvalho Chehab 			onoff ? "on" : "off");
315786baecfSMauro Carvalho Chehab 	b_pid[0] = (2 * index) + 4;
316786baecfSMauro Carvalho Chehab 	b_pid[1] = onoff ? (pid & 0xff) : (0x00);
317786baecfSMauro Carvalho Chehab 	b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00);
318786baecfSMauro Carvalho Chehab 	i2c_transfer(&adap->dev->i2c_adap, msg, 1);
319786baecfSMauro Carvalho Chehab 	return 0;
320786baecfSMauro Carvalho Chehab }
321786baecfSMauro Carvalho Chehab 
opera1_pid_filter_control(struct dvb_usb_adapter * adap,int onoff)322786baecfSMauro Carvalho Chehab static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff)
323786baecfSMauro Carvalho Chehab {
324786baecfSMauro Carvalho Chehab 	int u = 0x04;
325786baecfSMauro Carvalho Chehab 	u8 b_pid[3];
326786baecfSMauro Carvalho Chehab 	struct i2c_msg msg[] = {
327786baecfSMauro Carvalho Chehab 		{.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3},
328786baecfSMauro Carvalho Chehab 	};
329786baecfSMauro Carvalho Chehab 	if (dvb_usb_opera1_debug)
330786baecfSMauro Carvalho Chehab 		info("%s hw-pidfilter", onoff ? "enable" : "disable");
331786baecfSMauro Carvalho Chehab 	for (; u < 0x7e; u += 2) {
332786baecfSMauro Carvalho Chehab 		b_pid[0] = u;
333786baecfSMauro Carvalho Chehab 		b_pid[1] = 0;
334786baecfSMauro Carvalho Chehab 		b_pid[2] = 0x80;
335786baecfSMauro Carvalho Chehab 		i2c_transfer(&adap->dev->i2c_adap, msg, 1);
336786baecfSMauro Carvalho Chehab 	}
337786baecfSMauro Carvalho Chehab 	return 0;
338786baecfSMauro Carvalho Chehab }
339786baecfSMauro Carvalho Chehab 
340786baecfSMauro Carvalho Chehab static struct rc_map_table rc_map_opera1_table[] = {
341786baecfSMauro Carvalho Chehab 	{0x5fa0, KEY_1},
342786baecfSMauro Carvalho Chehab 	{0x51af, KEY_2},
343786baecfSMauro Carvalho Chehab 	{0x5da2, KEY_3},
344786baecfSMauro Carvalho Chehab 	{0x41be, KEY_4},
345786baecfSMauro Carvalho Chehab 	{0x0bf5, KEY_5},
346786baecfSMauro Carvalho Chehab 	{0x43bd, KEY_6},
347786baecfSMauro Carvalho Chehab 	{0x47b8, KEY_7},
348786baecfSMauro Carvalho Chehab 	{0x49b6, KEY_8},
349786baecfSMauro Carvalho Chehab 	{0x05fa, KEY_9},
350786baecfSMauro Carvalho Chehab 	{0x45ba, KEY_0},
351786baecfSMauro Carvalho Chehab 	{0x09f6, KEY_CHANNELUP},	/*chanup */
352786baecfSMauro Carvalho Chehab 	{0x1be5, KEY_CHANNELDOWN},	/*chandown */
353786baecfSMauro Carvalho Chehab 	{0x5da3, KEY_VOLUMEDOWN},	/*voldown */
354786baecfSMauro Carvalho Chehab 	{0x5fa1, KEY_VOLUMEUP},		/*volup */
355786baecfSMauro Carvalho Chehab 	{0x07f8, KEY_SPACE},		/*tab */
356786baecfSMauro Carvalho Chehab 	{0x1fe1, KEY_OK},		/*play ok */
357786baecfSMauro Carvalho Chehab 	{0x1be4, KEY_ZOOM},		/*zoom */
358786baecfSMauro Carvalho Chehab 	{0x59a6, KEY_MUTE},		/*mute */
359786baecfSMauro Carvalho Chehab 	{0x5ba5, KEY_RADIO},		/*tv/f */
360786baecfSMauro Carvalho Chehab 	{0x19e7, KEY_RECORD},		/*rec */
361786baecfSMauro Carvalho Chehab 	{0x01fe, KEY_STOP},		/*Stop */
362786baecfSMauro Carvalho Chehab 	{0x03fd, KEY_PAUSE},		/*pause */
363786baecfSMauro Carvalho Chehab 	{0x03fc, KEY_SCREEN},		/*<- -> */
364786baecfSMauro Carvalho Chehab 	{0x07f9, KEY_CAMERA},		/*capture */
365786baecfSMauro Carvalho Chehab 	{0x47b9, KEY_ESC},		/*exit */
366786baecfSMauro Carvalho Chehab 	{0x43bc, KEY_POWER2},		/*power */
367786baecfSMauro Carvalho Chehab };
368786baecfSMauro Carvalho Chehab 
opera1_rc_query(struct dvb_usb_device * dev,u32 * event,int * state)369786baecfSMauro Carvalho Chehab static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state)
370786baecfSMauro Carvalho Chehab {
371786baecfSMauro Carvalho Chehab 	struct opera1_state *opst = dev->priv;
372786baecfSMauro Carvalho Chehab 	u8 rcbuffer[32];
373786baecfSMauro Carvalho Chehab 	const u16 startmarker1 = 0x10ed;
374786baecfSMauro Carvalho Chehab 	const u16 startmarker2 = 0x11ec;
375786baecfSMauro Carvalho Chehab 	struct i2c_msg read_remote[] = {
376786baecfSMauro Carvalho Chehab 		{.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32},
377786baecfSMauro Carvalho Chehab 	};
378786baecfSMauro Carvalho Chehab 	int i = 0;
379786baecfSMauro Carvalho Chehab 	u32 send_key = 0;
380786baecfSMauro Carvalho Chehab 
381786baecfSMauro Carvalho Chehab 	if (i2c_transfer(&dev->i2c_adap, read_remote, 1) == 1) {
382786baecfSMauro Carvalho Chehab 		for (i = 0; i < 32; i++) {
383786baecfSMauro Carvalho Chehab 			if (rcbuffer[i])
384786baecfSMauro Carvalho Chehab 				send_key |= 1;
385786baecfSMauro Carvalho Chehab 			if (i < 31)
386786baecfSMauro Carvalho Chehab 				send_key = send_key << 1;
387786baecfSMauro Carvalho Chehab 		}
388786baecfSMauro Carvalho Chehab 		if (send_key & 0x8000)
389786baecfSMauro Carvalho Chehab 			send_key = (send_key << 1) | (send_key >> 15 & 0x01);
390786baecfSMauro Carvalho Chehab 
391786baecfSMauro Carvalho Chehab 		if (send_key == 0xffff && opst->last_key_pressed != 0) {
392786baecfSMauro Carvalho Chehab 			*state = REMOTE_KEY_REPEAT;
393786baecfSMauro Carvalho Chehab 			*event = opst->last_key_pressed;
394786baecfSMauro Carvalho Chehab 			return 0;
395786baecfSMauro Carvalho Chehab 		}
396786baecfSMauro Carvalho Chehab 		for (; send_key != 0;) {
397786baecfSMauro Carvalho Chehab 			if (send_key >> 16 == startmarker2) {
398786baecfSMauro Carvalho Chehab 				break;
399786baecfSMauro Carvalho Chehab 			} else if (send_key >> 16 == startmarker1) {
400786baecfSMauro Carvalho Chehab 				send_key =
401786baecfSMauro Carvalho Chehab 					(send_key & 0xfffeffff) | (startmarker1 << 16);
402786baecfSMauro Carvalho Chehab 				break;
403786baecfSMauro Carvalho Chehab 			} else
404786baecfSMauro Carvalho Chehab 				send_key >>= 1;
405786baecfSMauro Carvalho Chehab 		}
406786baecfSMauro Carvalho Chehab 
407786baecfSMauro Carvalho Chehab 		if (send_key == 0)
408786baecfSMauro Carvalho Chehab 			return 0;
409786baecfSMauro Carvalho Chehab 
410786baecfSMauro Carvalho Chehab 		send_key = (send_key & 0xffff) | 0x0100;
411786baecfSMauro Carvalho Chehab 
412786baecfSMauro Carvalho Chehab 		for (i = 0; i < ARRAY_SIZE(rc_map_opera1_table); i++) {
413786baecfSMauro Carvalho Chehab 			if (rc5_scan(&rc_map_opera1_table[i]) == (send_key & 0xffff)) {
414786baecfSMauro Carvalho Chehab 				*state = REMOTE_KEY_PRESSED;
415786baecfSMauro Carvalho Chehab 				*event = rc_map_opera1_table[i].keycode;
416786baecfSMauro Carvalho Chehab 				opst->last_key_pressed =
417786baecfSMauro Carvalho Chehab 					rc_map_opera1_table[i].keycode;
418786baecfSMauro Carvalho Chehab 				break;
419786baecfSMauro Carvalho Chehab 			}
420786baecfSMauro Carvalho Chehab 			opst->last_key_pressed = 0;
421786baecfSMauro Carvalho Chehab 		}
422786baecfSMauro Carvalho Chehab 	} else
423786baecfSMauro Carvalho Chehab 		*state = REMOTE_NO_KEY_PRESSED;
424786baecfSMauro Carvalho Chehab 	return 0;
425786baecfSMauro Carvalho Chehab }
426786baecfSMauro Carvalho Chehab 
42722127ac8SMauro Carvalho Chehab enum {
42822127ac8SMauro Carvalho Chehab 	CYPRESS_OPERA1_COLD,
42922127ac8SMauro Carvalho Chehab 	OPERA1_WARM,
43022127ac8SMauro Carvalho Chehab };
43122127ac8SMauro Carvalho Chehab 
432786baecfSMauro Carvalho Chehab static struct usb_device_id opera1_table[] = {
43322127ac8SMauro Carvalho Chehab 	DVB_USB_DEV(CYPRESS, CYPRESS_OPERA1_COLD),
43422127ac8SMauro Carvalho Chehab 	DVB_USB_DEV(OPERA1, OPERA1_WARM),
435786baecfSMauro Carvalho Chehab 	{ }
436786baecfSMauro Carvalho Chehab };
437786baecfSMauro Carvalho Chehab 
438786baecfSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, opera1_table);
439786baecfSMauro Carvalho Chehab 
opera1_read_mac_address(struct dvb_usb_device * d,u8 mac[6])440786baecfSMauro Carvalho Chehab static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
441786baecfSMauro Carvalho Chehab {
442*f7e0f1f5SZhang Shurong 	int ret;
443786baecfSMauro Carvalho Chehab 	u8 command[] = { READ_MAC_ADDR };
444*f7e0f1f5SZhang Shurong 	ret = opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG);
445*f7e0f1f5SZhang Shurong 	if (ret)
446*f7e0f1f5SZhang Shurong 		return ret;
447*f7e0f1f5SZhang Shurong 	ret = opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG);
448*f7e0f1f5SZhang Shurong 	if (ret)
449*f7e0f1f5SZhang Shurong 		return ret;
450786baecfSMauro Carvalho Chehab 	return 0;
451786baecfSMauro Carvalho Chehab }
opera1_xilinx_load_firmware(struct usb_device * dev,const char * filename)452786baecfSMauro Carvalho Chehab static int opera1_xilinx_load_firmware(struct usb_device *dev,
453786baecfSMauro Carvalho Chehab 				       const char *filename)
454786baecfSMauro Carvalho Chehab {
455786baecfSMauro Carvalho Chehab 	const struct firmware *fw = NULL;
456786baecfSMauro Carvalho Chehab 	u8 *b, *p;
457786baecfSMauro Carvalho Chehab 	int ret = 0, i,fpgasize=40;
458786baecfSMauro Carvalho Chehab 	u8 testval;
459786baecfSMauro Carvalho Chehab 	info("start downloading fpga firmware %s",filename);
460786baecfSMauro Carvalho Chehab 
461786baecfSMauro Carvalho Chehab 	if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) {
462fe63a1a6SMauro Carvalho Chehab 		err("did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware",
463786baecfSMauro Carvalho Chehab 			filename);
464786baecfSMauro Carvalho Chehab 		return ret;
465786baecfSMauro Carvalho Chehab 	} else {
466786baecfSMauro Carvalho Chehab 		p = kmalloc(fw->size, GFP_KERNEL);
467786baecfSMauro Carvalho Chehab 		opera1_xilinx_rw(dev, 0xbc, 0x00, &testval, 1, OPERA_READ_MSG);
468786baecfSMauro Carvalho Chehab 		if (p != NULL && testval != 0x67) {
469786baecfSMauro Carvalho Chehab 
470786baecfSMauro Carvalho Chehab 			u8 reset = 0, fpga_command = 0;
471786baecfSMauro Carvalho Chehab 			memcpy(p, fw->data, fw->size);
472786baecfSMauro Carvalho Chehab 			/* clear fpga ? */
473786baecfSMauro Carvalho Chehab 			opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1,
474786baecfSMauro Carvalho Chehab 					 OPERA_WRITE_MSG);
475786baecfSMauro Carvalho Chehab 			for (i = 0; i < fw->size;) {
476786baecfSMauro Carvalho Chehab 				if ( (fw->size - i) <fpgasize){
477786baecfSMauro Carvalho Chehab 				    fpgasize=fw->size-i;
478786baecfSMauro Carvalho Chehab 				}
479786baecfSMauro Carvalho Chehab 				b = (u8 *) p + i;
480786baecfSMauro Carvalho Chehab 				if (opera1_xilinx_rw
481786baecfSMauro Carvalho Chehab 					(dev, OPERA_WRITE_FX2, 0x0, b , fpgasize,
482786baecfSMauro Carvalho Chehab 						OPERA_WRITE_MSG) != fpgasize
483786baecfSMauro Carvalho Chehab 					) {
484786baecfSMauro Carvalho Chehab 					err("error while transferring firmware");
485786baecfSMauro Carvalho Chehab 					ret = -EINVAL;
486786baecfSMauro Carvalho Chehab 					break;
487786baecfSMauro Carvalho Chehab 				}
488786baecfSMauro Carvalho Chehab 				i = i + fpgasize;
489786baecfSMauro Carvalho Chehab 			}
490786baecfSMauro Carvalho Chehab 			/* restart the CPU */
491786baecfSMauro Carvalho Chehab 			if (ret || opera1_xilinx_rw
492786baecfSMauro Carvalho Chehab 					(dev, 0xa0, 0xe600, &reset, 1,
493786baecfSMauro Carvalho Chehab 					OPERA_WRITE_MSG) != 1) {
494786baecfSMauro Carvalho Chehab 				err("could not restart the USB controller CPU.");
495786baecfSMauro Carvalho Chehab 				ret = -EINVAL;
496786baecfSMauro Carvalho Chehab 			}
497786baecfSMauro Carvalho Chehab 		}
498786baecfSMauro Carvalho Chehab 	}
499786baecfSMauro Carvalho Chehab 	kfree(p);
500786baecfSMauro Carvalho Chehab 	release_firmware(fw);
501786baecfSMauro Carvalho Chehab 	return ret;
502786baecfSMauro Carvalho Chehab }
503786baecfSMauro Carvalho Chehab 
504786baecfSMauro Carvalho Chehab static struct dvb_usb_device_properties opera1_properties = {
505786baecfSMauro Carvalho Chehab 	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
506786baecfSMauro Carvalho Chehab 	.usb_ctrl = CYPRESS_FX2,
507786baecfSMauro Carvalho Chehab 	.firmware = "dvb-usb-opera-01.fw",
508786baecfSMauro Carvalho Chehab 	.size_of_priv = sizeof(struct opera1_state),
509786baecfSMauro Carvalho Chehab 
510786baecfSMauro Carvalho Chehab 	.power_ctrl = opera1_power_ctrl,
511786baecfSMauro Carvalho Chehab 	.i2c_algo = &opera1_i2c_algo,
512786baecfSMauro Carvalho Chehab 
513786baecfSMauro Carvalho Chehab 	.rc.legacy = {
514786baecfSMauro Carvalho Chehab 		.rc_map_table = rc_map_opera1_table,
515786baecfSMauro Carvalho Chehab 		.rc_map_size = ARRAY_SIZE(rc_map_opera1_table),
516786baecfSMauro Carvalho Chehab 		.rc_interval = 200,
517786baecfSMauro Carvalho Chehab 		.rc_query = opera1_rc_query,
518786baecfSMauro Carvalho Chehab 	},
519786baecfSMauro Carvalho Chehab 	.read_mac_address = opera1_read_mac_address,
520786baecfSMauro Carvalho Chehab 	.generic_bulk_ctrl_endpoint = 0x00,
521786baecfSMauro Carvalho Chehab 	/* parameter for the MPEG2-data transfer */
522786baecfSMauro Carvalho Chehab 	.num_adapters = 1,
523786baecfSMauro Carvalho Chehab 	.adapter = {
524786baecfSMauro Carvalho Chehab 		{
525786baecfSMauro Carvalho Chehab 		.num_frontends = 1,
526786baecfSMauro Carvalho Chehab 		.fe = {{
527786baecfSMauro Carvalho Chehab 			.frontend_attach = opera1_frontend_attach,
528786baecfSMauro Carvalho Chehab 			.streaming_ctrl = opera1_streaming_ctrl,
529786baecfSMauro Carvalho Chehab 			.tuner_attach = opera1_tuner_attach,
530786baecfSMauro Carvalho Chehab 			.caps =
531786baecfSMauro Carvalho Chehab 				DVB_USB_ADAP_HAS_PID_FILTER |
532786baecfSMauro Carvalho Chehab 				DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
533786baecfSMauro Carvalho Chehab 			.pid_filter = opera1_pid_filter,
534786baecfSMauro Carvalho Chehab 			.pid_filter_ctrl = opera1_pid_filter_control,
535786baecfSMauro Carvalho Chehab 			.pid_filter_count = 252,
536786baecfSMauro Carvalho Chehab 			.stream = {
537786baecfSMauro Carvalho Chehab 				.type = USB_BULK,
538786baecfSMauro Carvalho Chehab 				.count = 10,
539786baecfSMauro Carvalho Chehab 				.endpoint = 0x82,
540786baecfSMauro Carvalho Chehab 				.u = {
541786baecfSMauro Carvalho Chehab 					.bulk = {
542786baecfSMauro Carvalho Chehab 						.buffersize = 4096,
543786baecfSMauro Carvalho Chehab 					}
544786baecfSMauro Carvalho Chehab 				}
545786baecfSMauro Carvalho Chehab 			},
546786baecfSMauro Carvalho Chehab 		}},
547786baecfSMauro Carvalho Chehab 		}
548786baecfSMauro Carvalho Chehab 	},
549786baecfSMauro Carvalho Chehab 	.num_device_descs = 1,
550786baecfSMauro Carvalho Chehab 	.devices = {
551786baecfSMauro Carvalho Chehab 		{"Opera1 DVB-S USB2.0",
55222127ac8SMauro Carvalho Chehab 			{&opera1_table[CYPRESS_OPERA1_COLD], NULL},
55322127ac8SMauro Carvalho Chehab 			{&opera1_table[OPERA1_WARM], NULL},
554786baecfSMauro Carvalho Chehab 		},
555786baecfSMauro Carvalho Chehab 	}
556786baecfSMauro Carvalho Chehab };
557786baecfSMauro Carvalho Chehab 
opera1_probe(struct usb_interface * intf,const struct usb_device_id * id)558786baecfSMauro Carvalho Chehab static int opera1_probe(struct usb_interface *intf,
559786baecfSMauro Carvalho Chehab 			const struct usb_device_id *id)
560786baecfSMauro Carvalho Chehab {
561786baecfSMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(intf);
562786baecfSMauro Carvalho Chehab 
56318d6a28aSHans Verkuil 	if (le16_to_cpu(udev->descriptor.idProduct) == USB_PID_OPERA1_WARM &&
56418d6a28aSHans Verkuil 	    le16_to_cpu(udev->descriptor.idVendor) == USB_VID_OPERA1 &&
565786baecfSMauro Carvalho Chehab 		opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga-01.fw") != 0
566786baecfSMauro Carvalho Chehab 	    ) {
567786baecfSMauro Carvalho Chehab 		return -EINVAL;
568786baecfSMauro Carvalho Chehab 	}
569786baecfSMauro Carvalho Chehab 
570786baecfSMauro Carvalho Chehab 	if (0 != dvb_usb_device_init(intf, &opera1_properties,
571786baecfSMauro Carvalho Chehab 				     THIS_MODULE, NULL, adapter_nr))
572786baecfSMauro Carvalho Chehab 		return -EINVAL;
573786baecfSMauro Carvalho Chehab 	return 0;
574786baecfSMauro Carvalho Chehab }
575786baecfSMauro Carvalho Chehab 
576786baecfSMauro Carvalho Chehab static struct usb_driver opera1_driver = {
577786baecfSMauro Carvalho Chehab 	.name = "opera1",
578786baecfSMauro Carvalho Chehab 	.probe = opera1_probe,
579786baecfSMauro Carvalho Chehab 	.disconnect = dvb_usb_device_exit,
580786baecfSMauro Carvalho Chehab 	.id_table = opera1_table,
581786baecfSMauro Carvalho Chehab };
582786baecfSMauro Carvalho Chehab 
583786baecfSMauro Carvalho Chehab module_usb_driver(opera1_driver);
584786baecfSMauro Carvalho Chehab 
585786baecfSMauro Carvalho Chehab MODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org");
586786baecfSMauro Carvalho Chehab MODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de");
587786baecfSMauro Carvalho Chehab MODULE_DESCRIPTION("Driver for Opera1 DVB-S device");
588786baecfSMauro Carvalho Chehab MODULE_VERSION("0.1");
589786baecfSMauro Carvalho Chehab MODULE_LICENSE("GPL");
590