1791cb0886290fb5c3a99bee4c3c88b50d78e010
[~shefty/rdma-dev.git] / drivers / media / dvb / dvb-usb / az6007.c
1 /* 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 "drxk.h"
7 #include "mt2063.h"
8 #include "dvb_ca_en50221.h"
9 #include "dvb-usb.h"
10
11 #define DVB_USB_LOG_PREFIX "az6007"
12
13 /* HACK: Should be moved to the right place */
14 #define USB_PID_AZUREWAVE_6007          0x0ccd
15 #define USB_PID_TERRATEC_H7             0x10b4
16
17 /* debug */
18 int dvb_usb_az6007_debug;
19 module_param_named(debug, dvb_usb_az6007_debug, int, 0644);
20 MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))."
21                  DVB_USB_DEBUG_STATUS);
22
23 #define deb_info(args...) dprintk(dvb_usb_az6007_debug, 0x01, args)
24 #define deb_xfer(args...) dprintk(dvb_usb_az6007_debug, 0x02, args)
25 #define deb_rc(args...)   dprintk(dvb_usb_az6007_debug, 0x04, args)
26 #define deb_fe(args...)   dprintk(dvb_usb_az6007_debug, 0x08, args)
27
28 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
29
30 struct az6007_device_state {
31         struct                  dvb_ca_en50221 ca;
32         struct                  mutex ca_mutex;
33         u8                      power_state;
34
35         /* Due to DRX-K - probably need changes */
36         int                     (*gate_ctrl) (struct dvb_frontend *, int);
37         struct                  semaphore pll_mutex;
38         bool                    dont_attach_fe1;
39 };
40
41 static struct drxk_config terratec_h7_drxk = {
42         .adr = 0x29,
43         .single_master = 1,
44         .no_i2c_bridge = 0,
45         .microcode_name = "dvb-usb-terratec-h5-drxk.fw",
46 };
47
48 static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
49 {
50         struct dvb_usb_adapter *adap = fe->sec_priv;
51         struct az6007_device_state *st;
52         int status;
53
54         info("%s: %s", __func__, enable ? "enable" : "disable");
55
56         if (!adap)
57                 return -EINVAL;
58
59         st = adap->priv;
60
61         if (!st)
62                 return -EINVAL;
63
64         if (enable) {
65 #if 0
66                 down(&st->pll_mutex);
67 #endif
68                 status = st->gate_ctrl(fe, 1);
69         } else {
70 #if 0
71                 status = st->gate_ctrl(fe, 0);
72 #endif
73                 up(&st->pll_mutex);
74         }
75         return status;
76 }
77
78 static struct mt2063_config az6007_mt2063_config = {
79         .tuner_address = 0x60,
80         .refclock = 36125000,
81 };
82
83 /* check for mutex FIXME */
84 static int az6007_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value,
85                             u16 index, u8 *b, int blen)
86 {
87         int ret = -1;
88
89         ret = usb_control_msg(d->udev,
90                               usb_rcvctrlpipe(d->udev, 0),
91                               req,
92                               USB_TYPE_VENDOR | USB_DIR_IN,
93                               value, index, b, blen, 5000);
94
95         if (ret < 0) {
96                 warn("usb in operation failed. (%d)", ret);
97                 return -EIO;
98         }
99
100         deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
101                  index);
102         debug_dump(b, blen, deb_xfer);
103
104         return ret;
105 }
106
107 static int az6007_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
108                              u16 index, u8 *b, int blen)
109 {
110         int ret;
111
112         deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
113                  index);
114         debug_dump(b, blen, deb_xfer);
115
116         if (blen > 64) {
117                 printk(KERN_ERR
118                        "az6007: doesn't suport I2C transactions longer than 64 bytes\n");
119                 return -EOPNOTSUPP;
120         }
121
122         ret = usb_control_msg(d->udev,
123                               usb_sndctrlpipe(d->udev, 0),
124                               req,
125                               USB_TYPE_VENDOR | USB_DIR_OUT,
126                               value, index, b, blen, 5000);
127         if (ret != blen) {
128                 warn("usb out operation failed. (%d)", ret);
129                 return -EIO;
130         }
131
132         return 0;
133 }
134
135 static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
136 {
137         return 0;
138 }
139
140 /* keys for the enclosed remote control */
141 static struct rc_map_table rc_map_az6007_table[] = {
142         {0x0001, KEY_1},
143         {0x0002, KEY_2},
144 };
145
146 /* remote control stuff (does not work with my box) */
147 static int az6007_rc_query(struct dvb_usb_device *d, u32 * event, int *state)
148 {
149         return 0;
150 #if 0
151         u8 key[10];
152         int i;
153
154         /* remove the following return to enabled remote querying */
155
156         az6007_usb_in_op(d, READ_REMOTE_REQ, 0, 0, key, 10);
157
158         deb_rc("remote query key: %x %d\n", key[1], key[1]);
159
160         if (key[1] == 0x44) {
161                 *state = REMOTE_NO_KEY_PRESSED;
162                 return 0;
163         }
164
165         for (i = 0; i < ARRAY_SIZE(az6007_rc_keys); i++)
166                 if (az6007_rc_keys[i].custom == key[1]) {
167                         *state = REMOTE_KEY_PRESSED;
168                         *event = az6007_rc_keys[i].event;
169                         break;
170                 }
171         return 0;
172 #endif
173 }
174
175 /*
176 int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
177 {
178         u8 v = onoff;
179         return az6007_usb_out_op(d,0xBC,v,3,NULL,1);
180 }
181 */
182
183 static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
184 {
185         az6007_usb_in_op(d, 0xb7, 6, 0, &mac[0], 6);
186         return 0;
187 }
188
189 static int az6007_frontend_poweron(struct dvb_usb_adapter *adap)
190 {
191         int ret;
192         u8 req;
193         u16 value;
194         u16 index;
195         int blen;
196
197         info("az6007_frontend_poweron adap=%p adap->dev=%p", adap, adap->dev);
198
199         req = 0xBC;
200         value = 1;              /* power on */
201         index = 3;
202         blen = 0;
203
204         ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
205         if (ret != 0) {
206                 err("az6007_frontend_poweron failed!!!");
207                 return -EIO;
208         }
209
210         msleep_interruptible(200);
211
212         req = 0xBC;
213         value = 0;              /* power off */
214         index = 3;
215         blen = 0;
216
217         ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
218         if (ret != 0) {
219                 err("az6007_frontend_poweron failed!!!");
220                 return -EIO;
221         }
222
223         msleep_interruptible(200);
224
225         req = 0xBC;
226         value = 1;              /* power on */
227         index = 3;
228         blen = 0;
229
230         ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
231         if (ret != 0) {
232                 err("az6007_frontend_poweron failed!!!");
233                 return -EIO;
234         }
235         info("az6007_frontend_poweron: OK");
236
237         return 0;
238 }
239
240 static int az6007_frontend_reset(struct dvb_usb_adapter *adap)
241 {
242         int ret;
243         u8 req;
244         u16 value;
245         u16 index;
246         int blen;
247
248         info("az6007_frontend_reset adap=%p adap->dev=%p", adap, adap->dev);
249
250         /* reset demodulator */
251         req = 0xC0;
252         value = 1;              /* high */
253         index = 3;
254         blen = 0;
255         ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
256         if (ret != 0) {
257                 err("az6007_frontend_reset failed 1 !!!");
258                 return -EIO;
259         }
260
261         req = 0xC0;
262         value = 0;              /* low */
263         index = 3;
264         blen = 0;
265         msleep_interruptible(200);
266         ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
267         if (ret != 0) {
268                 err("az6007_frontend_reset failed 2 !!!");
269                 return -EIO;
270         }
271         msleep_interruptible(200);
272         req = 0xC0;
273         value = 1;              /* high */
274         index = 3;
275         blen = 0;
276
277         ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
278         if (ret != 0) {
279                 err("az6007_frontend_reset failed 3 !!!");
280                 return -EIO;
281         }
282
283         msleep_interruptible(200);
284
285         info("reset az6007 frontend");
286
287         return 0;
288 }
289
290 static int az6007_led_on_off(struct usb_interface *intf, int onoff)
291 {
292         int ret = -1;
293         u8 req;
294         u16 value;
295         u16 index;
296         int blen;
297         /* TS through */
298         req = 0xBC;
299         value = onoff;
300         index = 0;
301         blen = 0;
302
303         ret = usb_control_msg(interface_to_usbdev(intf),
304                               usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
305                               req,
306                               USB_TYPE_VENDOR | USB_DIR_OUT,
307                               value, index, NULL, blen, 2000);
308
309         if (ret < 0) {
310                 warn("usb in operation failed. (%d)", ret);
311                 ret = -EIO;
312         } else
313                 ret = 0;
314
315         deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
316                  index);
317
318         return ret;
319 }
320
321 static int az6007_frontend_tsbypass(struct dvb_usb_adapter *adap, int onoff)
322 {
323         int ret;
324         u8 req;
325         u16 value;
326         u16 index;
327         int blen;
328         /* TS through */
329         req = 0xC7;
330         value = onoff;
331         index = 0;
332         blen = 0;
333
334         ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
335         if (ret != 0)
336                 return -EIO;
337         return 0;
338 }
339
340 static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
341 {
342         struct az6007_device_state *st = adap->priv;
343
344         int result;
345
346         BUG_ON(!st);
347
348         az6007_frontend_poweron(adap);
349         az6007_frontend_reset(adap);
350
351         info("az6007_frontend_attach: drxk");
352
353         adap->fe = dvb_attach(drxk_attach, &terratec_h7_drxk,
354                               &adap->dev->i2c_adap, &adap->fe2);
355         if (!adap->fe) {
356                 result = -EINVAL;
357                 goto out_free;
358         }
359
360         info("Setting hacks");
361
362         /* FIXME: do we need a pll semaphore? */
363         adap->fe->sec_priv = adap;
364         sema_init(&st->pll_mutex, 1);
365         st->gate_ctrl = adap->fe->ops.i2c_gate_ctrl;
366         adap->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
367         adap->fe2->id = 1;
368
369         info("az6007_frontend_attach: mt2063");
370         /* Attach mt2063 to DVB-C frontend */
371         if (adap->fe->ops.i2c_gate_ctrl)
372                 adap->fe->ops.i2c_gate_ctrl(adap->fe, 1);
373         if (!dvb_attach(mt2063_attach, adap->fe, &az6007_mt2063_config,
374                         &adap->dev->i2c_adap)) {
375                 result = -EINVAL;
376
377                 goto out_free;
378         }
379         if (adap->fe->ops.i2c_gate_ctrl)
380                 adap->fe->ops.i2c_gate_ctrl(adap->fe, 0);
381
382         /* Hack - needed due to drxk */
383         adap->fe2->tuner_priv = adap->fe->tuner_priv;
384         memcpy(&adap->fe2->ops.tuner_ops,
385                &adap->fe->ops.tuner_ops, sizeof(adap->fe->ops.tuner_ops));
386         return 0;
387
388 out_free:
389         if (adap->fe)
390                 dvb_frontend_detach(adap->fe);
391         adap->fe = NULL;
392         adap->fe2 = NULL;
393
394         return result;
395 }
396
397 static struct dvb_usb_device_properties az6007_properties;
398
399 static void az6007_usb_disconnect(struct usb_interface *intf)
400 {
401         dvb_usb_device_exit(intf);
402 }
403
404 /* I2C */
405 static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
406                            int num)
407 {
408         struct dvb_usb_device *d = i2c_get_adapdata(adap);
409         int i, j, len;
410         int ret = 0;
411         u16 index;
412         u16 value;
413         int length;
414         u8 req, addr;
415         u8 data[512];
416
417         if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
418                 return -EAGAIN;
419
420         for (i = 0; i < num; i++) {
421                 addr = msgs[i].addr << 1;
422                 if (((i + 1) < num)
423                     && (msgs[i].len == 1)
424                     && (!msgs[i].flags & I2C_M_RD)
425                     && (msgs[i + 1].flags & I2C_M_RD)
426                     && (msgs[i].addr == msgs[i + 1].addr)) {
427                         /*
428                          * A write + read xfer for the same address, where
429                          * the first xfer has just 1 byte length.
430                          * Need to join both into one operation
431                          */
432                         if (dvb_usb_az6007_debug & 2)
433                                 printk(KERN_DEBUG
434                                        "az6007 I2C xfer write+read addr=0x%x len=%d/%d: ",
435                                        addr, msgs[i].len, msgs[i + 1].len);
436                         req = 0xb9;
437                         index = msgs[i].buf[0];
438                         value = addr | (1 << 8);
439                         length = 6 + msgs[i + 1].len;
440                         len = msgs[i + 1].len;
441                         ret = az6007_usb_in_op(d, req, value, index, data,
442                                                length);
443                         if (ret >= len) {
444                                 for (j = 0; j < len; j++) {
445                                         msgs[i + 1].buf[j] = data[j + 5];
446                                         if (dvb_usb_az6007_debug & 2)
447                                                 printk(KERN_CONT
448                                                        "0x%02x ",
449                                                        msgs[i + 1].buf[j]);
450                                 }
451                         } else
452                                 ret = -EIO;
453                         i++;
454                 } else if (!(msgs[i].flags & I2C_M_RD)) {
455                         /* write bytes */
456                         if (dvb_usb_az6007_debug & 2)
457                                 printk(KERN_DEBUG
458                                        "az6007 I2C xfer write addr=0x%x len=%d: ",
459                                        addr, msgs[i].len);
460                         req = 0xbd;
461                         index = msgs[i].buf[0];
462                         value = addr | (1 << 8);
463                         length = msgs[i].len - 1;
464                         len = msgs[i].len - 1;
465                         if (dvb_usb_az6007_debug & 2)
466                                 printk(KERN_CONT "(0x%02x) ", msgs[i].buf[0]);
467                         for (j = 0; j < len; j++) {
468                                 data[j] = msgs[i].buf[j + 1];
469                                 if (dvb_usb_az6007_debug & 2)
470                                         printk(KERN_CONT "0x%02x ", data[j]);
471                         }
472                         ret =  az6007_usb_out_op(d, req, value, index, data,
473                                                  length);
474                 } else {
475                         /* read bytes */
476                         if (dvb_usb_az6007_debug & 2)
477                                 printk(KERN_DEBUG
478                                        "az6007 I2C xfer read addr=0x%x len=%d: ",
479                                        addr, msgs[i].len);
480                         req = 0xb9;
481                         index = msgs[i].buf[0];
482                         value = addr;
483                         length = msgs[i].len + 6;
484                         len = msgs[i].len;
485                         ret = az6007_usb_in_op(d, req, value, index, data,
486                                                length);
487                         for (j = 0; j < len; j++) {
488                                 msgs[i].buf[j] = data[j + 5];
489                                 if (dvb_usb_az6007_debug & 2)
490                                         printk(KERN_CONT
491                                                "0x%02x ", data[j + 5]);
492                         }
493                 }
494                 if (dvb_usb_az6007_debug & 2)
495                         printk(KERN_CONT "\n");
496                 if (ret < 0)
497                         goto err;
498         }
499 err:
500         mutex_unlock(&d->i2c_mutex);
501
502         if (ret < 0) {
503                 info("%s ERROR: %i\n", __func__, ret);
504                 return ret;
505         }
506         return num;
507 }
508
509 static u32 az6007_i2c_func(struct i2c_adapter *adapter)
510 {
511         return I2C_FUNC_I2C;
512 }
513
514 static struct i2c_algorithm az6007_i2c_algo = {
515         .master_xfer = az6007_i2c_xfer,
516         .functionality = az6007_i2c_func,
517 };
518
519 int az6007_identify_state(struct usb_device *udev,
520                           struct dvb_usb_device_properties *props,
521                           struct dvb_usb_device_description **desc, int *cold)
522 {
523         u8 b[16];
524         s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
525                                   0xb7, USB_TYPE_VENDOR | USB_DIR_IN, 6, 0, b,
526                                   6, USB_CTRL_GET_TIMEOUT);
527
528         info("FW GET_VERSION length: %d", ret);
529
530         *cold = ret <= 0;
531
532         info("cold: %d", *cold);
533         return 0;
534 }
535
536 static int az6007_usb_probe(struct usb_interface *intf,
537                             const struct usb_device_id *id)
538 {
539         az6007_led_on_off(intf, 0);
540
541         return dvb_usb_device_init(intf, &az6007_properties,
542                                    THIS_MODULE, NULL, adapter_nr);
543 }
544
545 static struct usb_device_id az6007_usb_table[] = {
546         {USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007)},
547         {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7)},
548         {0},
549 };
550
551 MODULE_DEVICE_TABLE(usb, az6007_usb_table);
552
553 static struct dvb_usb_device_properties az6007_properties = {
554         .caps = DVB_USB_IS_AN_I2C_ADAPTER,
555         .usb_ctrl = CYPRESS_FX2,
556         .firmware            = "dvb-usb-az6007-03.fw",
557         .no_reconnect        = 1,
558
559         .identify_state         = az6007_identify_state,
560         .num_adapters = 1,
561         .adapter = {
562                 {
563                         /* .caps             = DVB_USB_ADAP_RECEIVES_204_BYTE_TS, */
564                         .streaming_ctrl   = az6007_streaming_ctrl,
565                         .frontend_attach  = az6007_frontend_attach,
566
567                         /* parameter for the MPEG2-data transfer */
568                         .stream = {
569                                 .type = USB_BULK,
570                                 .count = 10,
571                                 .endpoint = 0x02,
572                                 .u = {
573                                         .bulk = {
574                                                 .buffersize = 4096,
575                                         }
576                                 }
577                         },
578                         .size_of_priv     = sizeof(struct az6007_device_state),
579                 }
580         },
581         /* .power_ctrl       = az6007_power_ctrl, */
582         .read_mac_address = az6007_read_mac_addr,
583
584         .rc.legacy = {
585                 .rc_map_table  = rc_map_az6007_table,
586                 .rc_map_size  = ARRAY_SIZE(rc_map_az6007_table),
587                 .rc_interval      = 400,
588                 .rc_query         = az6007_rc_query,
589         },
590         .i2c_algo         = &az6007_i2c_algo,
591
592         .num_device_descs = 2,
593         .devices = {
594                 { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)",
595                   .cold_ids = { &az6007_usb_table[0], NULL },
596                   .warm_ids = { NULL },
597                 },
598                 { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)",
599                   .cold_ids = { &az6007_usb_table[1], NULL },
600                   .warm_ids = { NULL },
601                 },
602                 { NULL },
603         }
604 };
605
606 /* usb specific object needed to register this driver with the usb subsystem */
607 static struct usb_driver az6007_usb_driver = {
608         .name           = "dvb_usb_az6007",
609         .probe          = az6007_usb_probe,
610         .disconnect = dvb_usb_device_exit,
611         /* .disconnect  = az6007_usb_disconnect, */
612         .id_table       = az6007_usb_table,
613 };
614
615 /* module stuff */
616 static int __init az6007_usb_module_init(void)
617 {
618         int result;
619         info("az6007 usb module init");
620
621         result = usb_register(&az6007_usb_driver);
622         if (result) {
623                 err("usb_register failed. (%d)", result);
624                 return result;
625         }
626
627         return 0;
628 }
629
630 static void __exit az6007_usb_module_exit(void)
631 {
632         /* deregister this driver from the USB subsystem */
633         info("az6007 usb module exit");
634         usb_deregister(&az6007_usb_driver);
635 }
636
637 module_init(az6007_usb_module_init);
638 module_exit(az6007_usb_module_exit);
639
640 MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
641 MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
642 MODULE_VERSION("1.1");
643 MODULE_LICENSE("GPL");