blob: a709cec16b00267cb58822677cbda346f66dbf4b [file] [log] [blame]
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -03001/* DVB USB compliant Linux driver for the AzureWave 6017 USB2.0 DVB-S
2 * receiver.
3 * see Documentation/dvb/README.dvb-usb for more information
4 */
5
6#include "az6007.h"
7#include "drxk.h"
8#include "mt2063.h"
9#include "dvb_ca_en50221.h"
10
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030011/* HACK: Should be moved to the right place */
12#define USB_PID_AZUREWAVE_6007 0xccd
13#define USB_PID_TERRATEC_H7 0x10b4
14
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030015/* debug */
16int dvb_usb_az6007_debug;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030017module_param_named(debug, dvb_usb_az6007_debug, int, 0644);
18MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))."
19 DVB_USB_DEBUG_STATUS);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030020
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030021DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
22
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030023struct az6007_device_state {
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030024 struct dvb_ca_en50221 ca;
25 struct mutex ca_mutex;
26 u8 power_state;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030027
28 /* Due to DRX-K - probably need changes */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030029 int (*gate_ctrl) (struct dvb_frontend *, int);
30 struct semaphore pll_mutex;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030031 bool dont_attach_fe1;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030032};
33
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030034static struct drxk_config terratec_h7_drxk = {
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030035 .adr = 0x29,
36 .single_master = 1,
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -030037 .no_i2c_bridge = 0,
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030038 .microcode_name = "dvb-usb-terratec-h5-drxk.fw",
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030039};
40
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030041static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
42{
43 struct dvb_usb_adapter *adap = fe->sec_priv;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030044 struct az6007_device_state *st;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030045 int status;
46
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030047 info("%s: %s", __func__, enable ? "enable" : "disable");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030048
49 if (!adap)
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030050 return -EINVAL;
51
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030052 st = adap->priv;
53
54 if (!st)
55 return -EINVAL;
56
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030057 if (enable) {
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030058#if 0
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030059 down(&st->pll_mutex);
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030060#endif
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030061 status = st->gate_ctrl(fe, 1);
62 } else {
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030063#if 0
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030064 status = st->gate_ctrl(fe, 0);
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030065#endif
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030066 up(&st->pll_mutex);
67 }
68 return status;
69}
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030070
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030071static struct mt2063_config az6007_mt2063_config = {
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030072 .tuner_address = 0x60,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030073 .refclock = 36125000,
74};
75
76/* check for mutex FIXME */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030077static int az6007_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value,
78 u16 index, u8 *b, int blen)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030079{
80 int ret = -1;
81
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030082 ret = usb_control_msg(d->udev,
83 usb_rcvctrlpipe(d->udev, 0),
84 req,
85 USB_TYPE_VENDOR | USB_DIR_IN,
86 value, index, b, blen, 5000);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030087
88 if (ret < 0) {
89 warn("usb in operation failed. (%d)", ret);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030090 return -EIO;
91 }
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030092
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030093 deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
94 index);
95 debug_dump(b, blen, deb_xfer);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030096
97 return ret;
98}
99
100static int az6007_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
101 u16 index, u8 *b, int blen)
102{
103 int ret;
104
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300105 deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
106 index);
107 debug_dump(b, blen, deb_xfer);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300108
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300109 if (blen > 64) {
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300110 printk(KERN_ERR
111 "az6007: doesn't suport I2C transactions longer than 64 bytes\n");
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300112 return -EOPNOTSUPP;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300113 }
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300114
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300115 ret = usb_control_msg(d->udev,
116 usb_sndctrlpipe(d->udev, 0),
117 req,
118 USB_TYPE_VENDOR | USB_DIR_OUT,
119 value, index, b, blen, 5000);
120 if (ret != blen) {
121 warn("usb out operation failed. (%d)", ret);
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300122 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300123 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300124
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300125 return 0;
126}
127
128static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
129{
130 return 0;
131}
132
133/* keys for the enclosed remote control */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300134static struct rc_map_table rc_map_az6007_table[] = {
135 {0x0001, KEY_1},
136 {0x0002, KEY_2},
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300137};
138
139/* remote control stuff (does not work with my box) */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300140static int az6007_rc_query(struct dvb_usb_device *d, u32 * event, int *state)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300141{
142 return 0;
143#if 0
144 u8 key[10];
145 int i;
146
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300147 /* remove the following return to enabled remote querying */
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300148
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300149 az6007_usb_in_op(d, READ_REMOTE_REQ, 0, 0, key, 10);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300150
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300151 deb_rc("remote query key: %x %d\n", key[1], key[1]);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300152
153 if (key[1] == 0x44) {
154 *state = REMOTE_NO_KEY_PRESSED;
155 return 0;
156 }
157
158 for (i = 0; i < ARRAY_SIZE(az6007_rc_keys); i++)
159 if (az6007_rc_keys[i].custom == key[1]) {
160 *state = REMOTE_KEY_PRESSED;
161 *event = az6007_rc_keys[i].event;
162 break;
163 }
164 return 0;
165#endif
166}
167
168/*
169int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
170{
171 u8 v = onoff;
172 return az6007_usb_out_op(d,0xBC,v,3,NULL,1);
173}
174*/
175
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300176static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300177{
178 az6007_usb_in_op(d, 0xb7, 6, 0, &mac[0], 6);
179 return 0;
180}
181
182static int az6007_frontend_poweron(struct dvb_usb_adapter *adap)
183{
184 int ret;
185 u8 req;
186 u16 value;
187 u16 index;
188 int blen;
189
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300190 info("az6007_frontend_poweron adap=%p adap->dev=%p", adap, adap->dev);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300191
192 req = 0xBC;
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300193 value = 1; /* power on */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300194 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300195 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300196
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300197 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
198 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300199 err("az6007_frontend_poweron failed!!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300200 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300201 }
202
203 msleep_interruptible(200);
204
205 req = 0xBC;
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300206 value = 0; /* power off */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300207 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300208 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300209
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300210 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
211 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300212 err("az6007_frontend_poweron failed!!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300213 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300214 }
215
216 msleep_interruptible(200);
217
218 req = 0xBC;
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300219 value = 1; /* power on */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300220 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300221 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300222
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300223 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
224 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300225 err("az6007_frontend_poweron failed!!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300226 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300227 }
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300228 info("az6007_frontend_poweron: OK");
229
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300230 return 0;
231}
232
233static int az6007_frontend_reset(struct dvb_usb_adapter *adap)
234{
235 int ret;
236 u8 req;
237 u16 value;
238 u16 index;
239 int blen;
240
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300241 info("az6007_frontend_reset adap=%p adap->dev=%p", adap, adap->dev);
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300242
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300243 /* reset demodulator */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300244 req = 0xC0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300245 value = 1; /* high */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300246 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300247 blen = 0;
248 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
249 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300250 err("az6007_frontend_reset failed 1 !!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300251 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300252 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300253
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300254 req = 0xC0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300255 value = 0; /* low */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300256 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300257 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300258 msleep_interruptible(200);
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300259 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
260 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300261 err("az6007_frontend_reset failed 2 !!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300262 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300263 }
264 msleep_interruptible(200);
265 req = 0xC0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300266 value = 1; /* high */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300267 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300268 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300269
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300270 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
271 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300272 err("az6007_frontend_reset failed 3 !!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300273 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300274 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300275
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300276 msleep_interruptible(200);
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300277
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300278 info("reset az6007 frontend");
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300279
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300280 return 0;
281}
282
283static int az6007_led_on_off(struct usb_interface *intf, int onoff)
284{
285 int ret = -1;
286 u8 req;
287 u16 value;
288 u16 index;
289 int blen;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300290 /* TS through */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300291 req = 0xBC;
292 value = onoff;
293 index = 0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300294 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300295
296 ret = usb_control_msg(interface_to_usbdev(intf),
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300297 usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
298 req,
299 USB_TYPE_VENDOR | USB_DIR_OUT,
300 value, index, NULL, blen, 2000);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300301
302 if (ret < 0) {
303 warn("usb in operation failed. (%d)", ret);
304 ret = -EIO;
305 } else
306 ret = 0;
307
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300308 deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
309 index);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300310
311 return ret;
312}
313
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300314static int az6007_frontend_tsbypass(struct dvb_usb_adapter *adap, int onoff)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300315{
316 int ret;
317 u8 req;
318 u16 value;
319 u16 index;
320 int blen;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300321 /* TS through */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300322 req = 0xC7;
323 value = onoff;
324 index = 0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300325 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300326
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300327 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
328 if (ret != 0)
329 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300330 return 0;
331}
332
333static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
334{
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300335 struct az6007_device_state *st = adap->priv;
336
337 int result;
338
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300339 BUG_ON(!st);
340
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300341 az6007_frontend_poweron(adap);
342 az6007_frontend_reset(adap);
343
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300344 info("az6007_frontend_attach: drxk");
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300345
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300346 adap->fe = dvb_attach(drxk_attach, &terratec_h7_drxk,
347 &adap->dev->i2c_adap, &adap->fe2);
348 if (!adap->fe) {
349 result = -EINVAL;
350 goto out_free;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300351 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300352
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300353 info("Setting hacks");
354
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300355 /* FIXME: do we need a pll semaphore? */
356 adap->fe->sec_priv = adap;
357 sema_init(&st->pll_mutex, 1);
358 st->gate_ctrl = adap->fe->ops.i2c_gate_ctrl;
359 adap->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
360 adap->fe2->id = 1;
361
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300362 info("az6007_frontend_attach: mt2063");
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300363 /* Attach mt2063 to DVB-C frontend */
364 if (adap->fe->ops.i2c_gate_ctrl)
365 adap->fe->ops.i2c_gate_ctrl(adap->fe, 1);
366 if (!dvb_attach(mt2063_attach, adap->fe, &az6007_mt2063_config,
367 &adap->dev->i2c_adap)) {
368 result = -EINVAL;
369
370 goto out_free;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300371 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300372 if (adap->fe->ops.i2c_gate_ctrl)
373 adap->fe->ops.i2c_gate_ctrl(adap->fe, 0);
374
375 /* Hack - needed due to drxk */
376 adap->fe2->tuner_priv = adap->fe->tuner_priv;
377 memcpy(&adap->fe2->ops.tuner_ops,
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300378 &adap->fe->ops.tuner_ops, sizeof(adap->fe->ops.tuner_ops));
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300379 return 0;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300380
381out_free:
382 if (adap->fe)
383 dvb_frontend_detach(adap->fe);
384 adap->fe = NULL;
385 adap->fe2 = NULL;
386
387 return result;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300388}
389
390static struct dvb_usb_device_properties az6007_properties;
391
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300392static void az6007_usb_disconnect(struct usb_interface *intf)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300393{
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300394 dvb_usb_device_exit(intf);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300395}
396
397/* I2C */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300398static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
399 int num)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300400{
401 struct dvb_usb_device *d = i2c_get_adapdata(adap);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300402 int i, j, len;
403 int ret = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300404 u16 index;
405 u16 value;
406 int length;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300407 u8 req, addr;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300408 u8 data[512];
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300409
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300410 if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
411 return -EAGAIN;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300412
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300413 for (i = 0; i < num; i++) {
414 addr = msgs[i].addr << 1;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300415 if (((i + 1) < num)
416 && (msgs[i].len == 1)
417 && (!msgs[i].flags & I2C_M_RD)
418 && (msgs[i + 1].flags & I2C_M_RD)
419 && (msgs[i].addr == msgs[i + 1].addr)) {
420 /*
421 * A write + read xfer for the same address, where
422 * the first xfer has just 1 byte length.
423 * Need to join both into one operation
424 */
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300425 if (dvb_usb_az6007_debug & 2)
426 printk(KERN_DEBUG
427 "az6007 I2C xfer write+read addr=0x%x len=%d/%d: ",
428 addr, msgs[i].len, msgs[i + 1].len);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300429 req = 0xb9;
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300430 index = msgs[i].buf[0];
431 value = addr | (1 << 8);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300432 length = 6 + msgs[i + 1].len;
433 len = msgs[i + 1].len;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300434 ret = az6007_usb_in_op(d, req, value, index, data,
435 length);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300436 if (ret >= len) {
437 for (j = 0; j < len; j++) {
438 msgs[i + 1].buf[j] = data[j + 5];
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300439 if (dvb_usb_az6007_debug & 2)
440 printk(KERN_CONT
441 "0x%02x ",
442 msgs[i + 1].buf[j]);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300443 }
444 } else
445 ret = -EIO;
446 i++;
447 } else if (!(msgs[i].flags & I2C_M_RD)) {
448 /* write bytes */
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300449 if (dvb_usb_az6007_debug & 2)
450 printk(KERN_DEBUG
451 "az6007 I2C xfer write addr=0x%x len=%d: ",
452 addr, msgs[i].len);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300453 req = 0xbd;
454 index = msgs[i].buf[0];
455 value = addr | (1 << 8);
456 length = msgs[i].len - 1;
457 len = msgs[i].len - 1;
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300458 if (dvb_usb_az6007_debug & 2)
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300459 printk(KERN_CONT "(0x%02x) ", msgs[i].buf[0]);
460 for (j = 0; j < len; j++) {
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300461 data[j] = msgs[i].buf[j + 1];
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300462 if (dvb_usb_az6007_debug & 2)
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300463 printk(KERN_CONT "0x%02x ", data[j]);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300464 }
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300465 ret = az6007_usb_out_op(d, req, value, index, data,
466 length);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300467 } else {
468 /* read bytes */
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300469 if (dvb_usb_az6007_debug & 2)
470 printk(KERN_DEBUG
471 "az6007 I2C xfer read addr=0x%x len=%d: ",
472 addr, msgs[i].len);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300473 req = 0xb9;
474 index = msgs[i].buf[0];
475 value = addr;
476 length = msgs[i].len + 6;
477 len = msgs[i].len;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300478 ret = az6007_usb_in_op(d, req, value, index, data,
479 length);
480 for (j = 0; j < len; j++) {
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300481 msgs[i].buf[j] = data[j + 5];
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300482 if (dvb_usb_az6007_debug & 2)
483 printk(KERN_CONT
484 "0x%02x ", data[j + 5]);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300485 }
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300486 }
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300487 if (dvb_usb_az6007_debug & 2)
488 printk(KERN_CONT "\n");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300489 if (ret < 0)
490 goto err;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300491 }
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300492err:
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300493 mutex_unlock(&d->i2c_mutex);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300494
495 if (ret < 0) {
496 info("%s ERROR: %i\n", __func__, ret);
497 return ret;
498 }
499 return num;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300500}
501
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300502static u32 az6007_i2c_func(struct i2c_adapter *adapter)
503{
504 return I2C_FUNC_I2C;
505}
506
507static struct i2c_algorithm az6007_i2c_algo = {
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300508 .master_xfer = az6007_i2c_xfer,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300509 .functionality = az6007_i2c_func,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300510};
511
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300512int az6007_identify_state(struct usb_device *udev,
513 struct dvb_usb_device_properties *props,
514 struct dvb_usb_device_description **desc, int *cold)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300515{
516 u8 b[16];
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300517 s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
518 0xb7, USB_TYPE_VENDOR | USB_DIR_IN, 6, 0, b,
519 6, USB_CTRL_GET_TIMEOUT);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300520
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300521 info("FW GET_VERSION length: %d", ret);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300522
523 *cold = ret <= 0;
524
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300525 info("cold: %d", *cold);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300526 return 0;
527}
528
529static int az6007_usb_probe(struct usb_interface *intf,
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300530 const struct usb_device_id *id)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300531{
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300532 az6007_led_on_off(intf, 0);
533
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300534 return dvb_usb_device_init(intf, &az6007_properties,
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300535 THIS_MODULE, NULL, adapter_nr);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300536}
537
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300538static struct usb_device_id az6007_usb_table[] = {
539 {USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007)},
540 {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7)},
541 {0},
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300542};
543
544MODULE_DEVICE_TABLE(usb, az6007_usb_table);
545
546static struct dvb_usb_device_properties az6007_properties = {
547 .caps = DVB_USB_IS_AN_I2C_ADAPTER,
548 .usb_ctrl = CYPRESS_FX2,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300549 .firmware = "dvb-usb-az6007-03.fw",
550 .no_reconnect = 1,
551
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300552 .identify_state = az6007_identify_state,
553 .num_adapters = 1,
554 .adapter = {
555 {
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300556 /* .caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS, */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300557 .streaming_ctrl = az6007_streaming_ctrl,
558 .frontend_attach = az6007_frontend_attach,
559
560 /* parameter for the MPEG2-data transfer */
561 .stream = {
562 .type = USB_BULK,
563 .count = 10,
564 .endpoint = 0x02,
565 .u = {
566 .bulk = {
567 .buffersize = 4096,
568 }
569 }
570 },
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300571 .size_of_priv = sizeof(struct az6007_device_state),
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300572 }
573 },
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300574 /* .power_ctrl = az6007_power_ctrl, */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300575 .read_mac_address = az6007_read_mac_addr,
576
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300577 .rc.legacy = {
578 .rc_map_table = rc_map_az6007_table,
579 .rc_map_size = ARRAY_SIZE(rc_map_az6007_table),
580 .rc_interval = 400,
581 .rc_query = az6007_rc_query,
582 },
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300583 .i2c_algo = &az6007_i2c_algo,
584
585 .num_device_descs = 2,
586 .devices = {
587 { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)",
588 .cold_ids = { &az6007_usb_table[0], NULL },
589 .warm_ids = { NULL },
590 },
591 { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)",
592 .cold_ids = { &az6007_usb_table[1], NULL },
593 .warm_ids = { NULL },
594 },
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300595 { NULL },
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300596 }
597};
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300598
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300599/* usb specific object needed to register this driver with the usb subsystem */
600static struct usb_driver az6007_usb_driver = {
601 .name = "dvb_usb_az6007",
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300602 .probe = az6007_usb_probe,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300603 .disconnect = dvb_usb_device_exit,
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300604 /* .disconnect = az6007_usb_disconnect, */
605 .id_table = az6007_usb_table,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300606};
607
608/* module stuff */
609static int __init az6007_usb_module_init(void)
610{
611 int result;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300612 info("az6007 usb module init");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300613
614 result = usb_register(&az6007_usb_driver);
615 if (result) {
616 err("usb_register failed. (%d)", result);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300617 return result;
618 }
619
620 return 0;
621}
622
623static void __exit az6007_usb_module_exit(void)
624{
625 /* deregister this driver from the USB subsystem */
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300626 info("az6007 usb module exit");
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300627 usb_deregister(&az6007_usb_driver);
628}
629
630module_init(az6007_usb_module_init);
631module_exit(az6007_usb_module_exit);
632
633MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
634MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300635MODULE_VERSION("1.1");
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300636MODULE_LICENSE("GPL");